While the MERGE
syntax is nicer, and it seems to promise better atomicity and no race conditions (but doesn't, unless you add HOLDLOCK
, as this Dan Guzman post demonstrates), I still feel like it is better to hang onto the old-style, separate insert/update methodology. The primary reason is not that the syntax is hard to learn (which it is), or that it is difficult to work around the concurrency issues (it isn't), but rather that there are several unresolved bugs - even in SQL Server 2012 still - involving this operation. I point over a dozen of them out in this post that talks about yet another MERGE
bug that has been fixed recently. I also go into more detail in a cautionary tip posted here and several others chime in here.
As I suggested in a comment, I don't want to sound the alarms that the sky is falling, but I'm really not all that interested in switching to MERGE
until there are far fewer active bugs. So I would recommend sticking with an old-fashioned UPSERT
for now, even though the actual syntax you're using might not be the source of the performance problem you're having anyway.
UPDATE dest SET column1 = src.column1
FROM dbo.DestinationTable AS dest
INNER JOIN (some join) AS src
ON (some condition);
INSERT dest(...) SELECT cols
FROM (some join) AS src
WHERE NOT EXISTS
(
SELECT 1 FROM dbo.DestinationTable
WHERE key = src.key
);
For the performance issue, you'll want to look into your session's waits (Plan Explorer* can help with that, by firing up an Extended Events session for you), or at the very least, blocking_session_id
and wait_type_desc
in sys.dm_exec_requests
while the query is running.
*Disclaimer: I used to work for SQL Sentry.