views:

420

answers:

2

Some sample data:

DECLARE @TARGET TABLE ( ID INT, value INT ) ;
DECLARE @SOURCE TABLE ( ID INT, value INT )

INSERT INTO @TARGET VALUES ( 1, 213 )             
INSERT INTO @TARGET VALUES ( 2, 3 )             
INSERT INTO @TARGET VALUES ( 3, 310 )             
INSERT INTO @TARGET VALUES ( 4, 43 )                     

INSERT INTO @SOURCE VALUES ( 1, 134 )             
INSERT INTO @SOURCE VALUES ( 2, 34 )             
INSERT INTO @SOURCE VALUES ( 13, 310 )             
INSERT INTO @SOURCE VALUES ( 14, 43 )             
INSERT INTO @SOURCE VALUES ( 15,32 )             
INSERT INTO @SOURCE VALUES ( 16, 30 )             
INSERT INTO @SOURCE VALUES ( 17, 60 )             
INSERT INTO @SOURCE VALUES ( 18, 5 )                   


MERGE @TARGET t USING (SELECT * FROM @SOURCE) AS s ON (t.id = s.id)
WHEN NOT MATCHED THEN
INSERT VALUES (s.id,s.value);

SELECT * FROM @TARGET

So I'm having a target table , and a source table. What I want to accomplish is that when there is a large number of not matched items, to only insert the x top items with the highest value.
Using top on the merge itself won't work, because that would limit the whole source table, I want to do something like

WHEN NOT MATCHED 
LIMIT(5) AND ORDER BY Value DESC --only insert the 5 non-matches with the highest value
INSERT VALUES (s.id,s.value)

---- UPDATE ----
My MERGE statement also contains an WHEN MATCHED THEN statement:

WHEN MATCHED THEN
UPDATE SET t.value = s.value

this sadly negates the answers given by Ian and Dog...

+2  A: 

You can use SET ROWCOUNT n;

For example;

SET ROWCOUNT 4;
UPDATE Production.ProductInventory
SET Quantity = 400
WHERE Quantity < 300;

See; http://msdn.microsoft.com/en-us/library/ms188774.aspx

Or you can do

Insert to @Target 
Select top 5 s.id, s.value from @Source s 
order by s.value desc ... etc.
Dead account
I wrote a comment before stackoverflow crashed... and erased this comment , and some other comments on other questions :-(anyway I oversimplified the example, there is also a WHEN MATCHED THEN, which should always update. Using the rowcount, it would first do the WHEN MATCHED and if there are ...
Gidon
... less rows updated then stated in the rowcount, it will INSERT in the WHEN NOT MATCHED THEN. This is not what I want, I want to limit only the INSERT and not the UPDATEs
Gidon
Not familiar with "murge", I would think about doing "where s.id not in (select id from @source). You could even do that with a trick join where the right hand side is null..Meh.. :)
Dead account
+2  A: 

Isn't SET ROWCOUNT Deprecated, you could use the top clause if you do it like this:

;MERGE TOP (5) @TARGET t USING 
(SELECT TOP (100) PERCENT * FROM @SOURCE ORDER BY VALUE DESC) AS s ON (t.id = s.id)
WHEN NOT MATCHED 
THEN
INSERT VALUES (s.id,s.value);

SELECT * FROM @TARGET

The ORDER BY int the merge wont work unless you have a TOP Clause so using TOP (100) PERCENT tricks SQL into allowing the ordering.

Edit:

What about doing it in two steps?

;MERGE TOP (5) @TARGET t USING 
(SELECT TOP (100) PERCENT * FROM @SOURCE ORDER BY VALUE DESC) AS s ON (t.id = s.id)
WHEN NOT MATCHED 
THEN
INSERT VALUES (s.id,s.value);/*
WHEN MATCHED THEN
UPDATE SET t.value = s.value;*/

update t 
set t.Value = s.Value
from     @Target t
    join @Source s on t.ID = s.ID 
where t.Value <> s.Value

SELECT * FROM @TARGET
Dog Ears
you are right about the SET ROWCOUNT being deprecated. In my comment to Ian Quigley I wrote that my example is oversimplified, and left out the fact that I also have an WHEN MATCHED THEN statement which updates the target. The update should happen on all matched records, the insert should only ...
Gidon
... happen on the top x records with the highest value. Your example would work if I wouldn't have the other requirement.
Gidon
What about doing it in two steps see amended answer.
Dog Ears
I also thought about doing it like that. seems like that is the only solution.
Gidon