Question

I have a merge statement like below

    MERGE DESTINATION AS DST
 USING ( SELECT <Some_Columns> FROM TABLEA WITH(NOLOCK) INNER JOIN TableB .....

       ) AS SRC
   ON(
     <some conditions>
   )

 WHEN MATCHED THEN 
    UPDATE SET column1 = src.column1
              ...............
              ,Modified_By = @PackageName
              ,Modified_Date = GETDATE() 

 WHEN NOT MATCHED THEN 

    INSERT (<Some_Columns>)
    VALUES(<Some_Columns>)

         OUTPUT        
                  $action, inserted.key'inserted'
                   INTO @tableVar
                ;

For the first set of records (around 300,000) it is working perfectly and executing in just 30 seconds. But for the second set of records (around 300,000) it is taking more than an hour.

Two days back I have loaded 50 sets like that and the same query was working lightning fast, but from today it is damn slow. I have no idea what is going wrong.

Note: Query

SELECT FROM TABLEA WITH(NOLOCK) INNER JOIN TableB .....

is taking 20 seconds in all scenerios.

Was it helpful?

Solution

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top