THE SQL Server Blog Spot on the Web

Welcome to SQLblog.com - The SQL Server blog spot on the web Sign in | Join | Help
in Search

SSIS Junkie

Extracting insert, update, delete rowcounts from T-SQL MERGE

Just lately I’ve been using T-SQL’s MERGE statement (introduced in SQL Server 2008) and one thing that I needed to do was extract rowcounts for each DML operation (i.e. INSERT, UPDATE, DELETE) conducted by a MERGE. I was surprised to find that while @@ROWCOUNT is supported for MERGE, it only returns the total number of affected rows and there are no built in functions for getting the counts for each DML operation (although Aaron Bertrand has a Connect submission asking for it).

There is an easy way around this however; MERGE includes a function called $action which can be used in the OUTPUT clause to specify whether a row was inserted, updated or deleted. I cut a small bit of code that demos how it works which you can get at the bottom of this blog post. Here’s what it produces:

image

I hope its useful to someone!

If you want to learn more about the MERGE statement and the OUTPUT clause then head over and read Adam Machanic’s post Dr OUTPUT: Or how I learned to stop worrying and love the MERGE.

@Jamiet


Here’s the code!! Disclaimer: Take it or leave it – just don’t shout at me if there are any problems here (although constructive comments are welcomed)

set nocount on;

create table #t
(
id int
, name varchar(max)
);
go

insert #t values (1,'Jim'),(2,'Sarah'),(3,'Hels');
go

DECLARE @rowcounts TABLE
(
mergeAction nvarchar(10)
);
declare @insertCount int, @updateCount int, @deleteCount int;
merge into #t as tgt
using ( select 1 as id, 'James' as name
union
select 2, 'Sarah'
union
select 3, 'Helen'
union
select 4, 'Jack'
union
select 5, 'Annie') as src
on tgt.id = src.id
when matched and tgt.name = src.name
THEN DELETE
when matched and tgt.name <> src.name
THEN UPDATE SET tgt.name = src.name
when not matched
THEN insert values (src.id, src.name)
OUTPUT $action into @rowcounts;

select @insertcount=[INSERT]
, @updatecount=[UPDATE]
, @deletecount=[DELETE]
from (
select mergeAction,1 rows
from @rowcounts
)p
pivot
(
count(rows)
FOR mergeAction IN
( [INSERT], [UPDATE], [DELETE])
) as pvt
;
drop table #t;


print '@insertcount = ' + cast(@insertcount as varchar);
print '@updatecount = ' + cast(@updatecount as varchar);
print '@deletecount = ' + cast(@deletecount as varchar);


Published Sunday, August 30, 2009 1:54 AM by jamiet
Filed under: , ,

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Comments

 

Peso said:

Have you tried wrap the MERGE statement as a COMPOSABLE DML?

That way, you only have to insert the aggregated values into @rowcounts.

August 30, 2009 3:08 AM
 

Jason Haley said:

Interesting Finds: August 30, 2009

August 30, 2009 6:49 AM
 

jamiet said:

Hi Peso,

I have tried it, yeah. I got an error that I certainly wasn't expecting however: "The GROUP BY clause is not allowed when the FROM clause contains a nested INSERT, UPDATE, DELETE, or MERGE statement."

So yes, I can use composable DML to get a the contents of the OUTPUT clause but it seems I cannot aggregate them prior to the insertion. That's rather annoying.

-Jamie

P.S. Here's the code that throws that error if you care to run it:

set nocount on ;

--drop table #t

create table #t

(

id int

, name varchar(max)

);

go

insert #t values (1,'Jim'),(2,'Sarah'),(3,'Hels');

go

DECLARE @rowcounts TABLE

(

mergeAction nvarchar(10)

, numRows int

);

declare @insertCount int, @updateCount int, @deleteCount int;

insert @rowcounts

select x.mergeAction,COUNT(*)

from (

merge into #t as tgt

using ( select 1 as id, 'James' as name

union

select 2, 'Sarah'

union

select 3, 'Helen'

union

select 4, 'Jack'

union

select 5, 'Annie') as src

on tgt.id = src.id

when matched and tgt.name = src.name

THEN DELETE

when matched and tgt.name <> src.name

THEN UPDATE SET tgt.name = src.name

when not matched

THEN insert values (src.id, src.name)

OUTPUT $action as mergeAction

) x

group by x.mergeAction;

August 30, 2009 5:50 PM
 

Roji Thomas said:

The final selct can be simplified as

SELECT @insertcount=  SUM(CASE WHEN mergeAction = 'INSERT' THEN 1 ELSE 0 END),

   @updatecount = SUM(CASE WHEN mergeAction = 'UPDATE' THEN 1 ELSE 0 END),

   @deletecount  = SUM(CASE WHEN mergeAction = 'DELETE' THEN 1 ELSE 0 END)

FROM @rowcounts    

August 31, 2009 9:17 AM
 

SqlServerKudos said:

Kudos for a great Sql Server article - Trackback from SqlServerKudos

August 31, 2009 10:38 AM
 

Log Buffer #160: a Carnival of the Vanities for DBAs | Pythian Group Blog said:

September 4, 2009 1:00 PM
 

Log Buffer #160: a Carnival of the Vanities for DBAs « PlanetMysql.ru – ???????????????????? ?? ???????? MySQL said:

September 4, 2009 3:01 PM
 

Derek G. said:

Can anybody who has used GSS Group's <a href="http://www.gssgrp.com/VIGILANCE_XPress.html">web-based ETL software "Vigilance Xpress"</a> post some feedback on their experience with it, as i'm doing trying to narrow down best ETL choice for my employer.  Thanks!

October 14, 2009 1:43 PM

Leave a Comment

(required) 
(optional)
(required) 
Submit
Powered by Community Server (Commercial Edition), by Telligent Systems
  Privacy Statement