Sql Server 2008 MERGE - best way to get counts
Solution 1
You could specify an OUTPUT clause on your MERGE statement and get an output report of what's been done during MERGE.
MERGE (targetTable) AS t
USING (sourceTable) AS s
ON t.ID = s.ID
WHEN MATCHED THEN
(some statements)
WHEN NOT MATCHED THEN
(some statements)
OUTPUT
$action, inserted.ID 'inserted', deleted.ID 'deleted'
;
This will give you a row for each "action" (insert, update, delete) for each operation. If it's a lot of statements, you could also OUTPUT INTO @tableVar and then look at the table variable.
DECLARE @tableVar TABLE (MergeAction VARCHAR(20), InsertedID INT, DeletedID INT)
MERGE (targetTable) AS t
USING (sourceTable) AS s
ON t.ID = s.ID
WHEN MATCHED THEN
(some statements)
WHEN NOT MATCHED THEN
(some statements)
OUTPUT
$action, inserted.ID 'inserted', deleted.ID 'deleted' INTO @tableVar
;
SELECT MergeAction, COUNT(*)
FROM @tableVar
GROUP BY MergeAction
Check out the Books Online for details on the MERGE statement and the OUTPUT clause.
Marc
Solution 2
To extract into individual vars, can post process answer by marc_s using pivot:
declare
@mergeResultsTable table (MergeAction VARCHAR(20));
declare
@insertCount int,
@updateCount int,
@deleteCount int;
merge ...
output $action into @mergeResultsTable;
select @insertCount = [INSERT],
@updateCount = [UPDATE],
@deleteCount = [DELETE]
from (select 'NOOP' MergeAction -- row for null merge into null
union all
select * from @mergeResultsTable) mergeResultsPlusEmptyRow
pivot (count(MergeAction)
for MergeAction in ([INSERT],[UPDATE],[DELETE]))
as mergeResultsPivot;
The union 'noop' row can be removed if init vars to 0 or know that source or target table has >0 rows.
Solution 3
How about:
INSERT YourResultsTable (action, cnt)
SELECT action, count(*)
FROM
(
MERGE (targetTable) AS t
USING (sourceTable) AS s
ON t.ID = s.ID
WHEN MATCHED THEN (some statements)
WHEN NOT MATCHED THEN (some statements)
OUTPUT $action as action, inserted.ID as ins, deleted.ID as del
) m
GROUP BY action;
[Edit] Ok, so try:
INSERT YourResultsTable (action)
SELECT action
FROM
(
MERGE (targetTable) AS t
USING (sourceTable) AS s
ON t.ID = s.ID
WHEN MATCHED THEN (some statements)
WHEN NOT MATCHED THEN (some statements)
OUTPUT $action as action, inserted.ID as ins, deleted.ID as del
) m;
(and then count the results)
Rob
eidylon
Software developer working primarily in VB.NET, C# and Sql Server. Occasionally working in ORACLE. I love RegEx and jQuery, and have just started playing around with WebAPI. Hobbyist prop maker. Have created several Mass Effect replica weapons as well as a number of custom-designed commission pieces. Projects and pictures on my Facebook page... https://www.facebook.com/darkchannelprops
Updated on December 03, 2020Comments
-
eidylon over 3 years
I'm wondering what y'alls would recommend as the best way to go about getting the action counts from a MERGE statement in Sql Server.
So, i.e. I run a MERGE which does some inserts, some updates and some deletes, ... I would like to be able to find out HOW MANY inserts, HOW MANY updates and How Many deletes.
What'd be the best way to do this?
-
eidylon over 14 yearsIs it possible somehow to do this in one statement, possibly with a group by? so that the output bit would be something sort of along the lines of > OUTPUT $ACTION, COUNT(1) GROUP BY $ACTION ?
-
marc_s over 14 yearsNo, you cannot do this. You can either just OUTPUT to the SSMS output window, or into a table variable - don't over-stretch the OUTPUT clause!! :-)
-
eidylon over 14 yearsAh well... Actually, does OUTPUT put out a table variable, or a resultset? Would it be possible to wrap the MERGE statement with an OUTPUT clause inside a SELECT statement as the source (as a subquery) and then have that outer SELECT do the aggregation? ... Sounds possible. I may have to play with that.
-
marc_s over 14 yearsOUTPUT can send its output directly to your result grid in SSMS, or into a table variable - that's it, according to the MSDN library. You cannot do any of the trickery you want to do - you'll have to use the steps as I showed them - sorry, you're trying to do something that SQL Server does not support
-
marc_s over 14 yearsYou cannot seem to use the MERGE statement in either a SELECT * FROM (MERGE as a subselect) or a Common Table Expression (WITH MergeOutput AS (MERGE:......)SELECT MergeAction.....) - it just doesn't work that way. You can output your values into the output window, a table variable, or a new table (on disk) - but you cannot use it directly as an output result set.
-
marc_s over 14 yearsNope: Msg 10717, Level 15, State 1, Line 48 The GROUP BY clause is not allowed when the FROM clause contains a nested INSERT, UPDATE, DELETE, or MERGE statement.
-
marc_s over 14 yearsTested on SQL Server 2008 Developer Edition
-
eidylon over 14 yearsI messed around, with this solution and got it working for my needs. Thanks!
-
MikeTeeVee about 10 yearsI used your edit, then took that table you inserted into and aggregated the individual actions into SUM-Totals using Case-Statements and logged those results. It works great! It stinks that SQL Server forces me to Insert the merge-results into a table-variable instead of aggregating and setting my int-variables directly in the outermost Select.
-
bjnr over 9 yearsmarc_S: what about the case you want to merge million rows? Is it that table variable the most efficient way to perform the count?
-
marc_s over 9 years@MihaiBejenariu: table variables are highly inefficient for large amounts of data - the query optimizer always assumes they contain one row of data only, and thus query execution can go awfully wrong due to a highly inefficient execution plan. Use a proper temp table instead in such a scenario
-
bjnr over 9 years@marc_s: just testing a scenario with 3M rows: it takes 18s to perform the merge, 36 to perform the merge + count from temp table, 39s to perform the merge + count from table variable. It seems it doubles the time in order to perform the count.
-
Ross Presser over 4 years@MihaiBejenariu - add a clustered index to your table variable and it will speed up the counting.