Click here to Skip to main content
15,896,348 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
I'm using the SQL Server merge statement to update one table from another (if a record in the target matches a record in the source, nothing happens, otherwise an insert is performed.

Theoretically, if I run the same query twice in a row, there shouldn't be any inserts performed on the 2nd run of the query, but I'm getting a handful of inserts that are still being performed. I have no idea why.

When I compare the record that already exists with the record I'm trying to insert, the joined column values are identical (which is supposed to indicate a match, and thus no insert), yet the Merge statement still inserts the source record.

Every time I execute the Merge statement, the same handful of records are re-inserted.

What I have tried:

I've been playing with this code. I've updated the code below to show its current state. Please review the comments in the code block for the weirdness:

SQL
-- I'm using this variable to indicate when a record was inserted
DECLARE @nextOrdinal INT = (SELECT CASE WHEN MAX(InsertOrdinal) IS NULL THEN 1 
                                        ELSE MAX(InsertOrdinal)+1 
                                        END 
                            FROM [Essentris].[dbo].[VancoMycin]);
    
-- create a temporary table to hold our grouped/normalized data
IF OBJECT_ID('tempdb..#tempVanco') IS NOT NULL
BEGIN
    DROP TABLE #tempVanco;
END
        
-- this temp table holds our grouped and normalized data
CREATE TABLE #tempVanco
(
    [ABX_NAME]      [nvarchar](255) NULL,
    [ROUTE]         [nvarchar](255) NULL,
    [DELIVERY_TIME] [datetime]      NULL,
    [HOSPNO]        [int]             NULL,
    [PTNAME]        [nvarchar](255) NULL,
    [UNIT]          [nvarchar](255) NULL,
    [ATTENDING]     [nvarchar](255) NULL,
    [SERVICE]       [nvarchar](255) NULL,
    [ADX]           [nvarchar](255) NULL
);


-- Normalize the data so that there are is no unexpected stuff in any of the fields. This 
-- also keeps me from having to do this further down in the code in the cte (this is a 
-- desperation measure after fighting with this for DAYS)

update  [Essentris].[dbo].[IMPORTED_Vanco]
SET     [ABX_NAME] = UPPER(RTRIM(LTRIM([ABX_NAME])))
        ,[ROUTE]    = UPPER(RTRIM(LTRIM([ROUTE])))
        ,[PTNAME]   = UPPER(RTRIM(LTRIM([PTNAME])))
        ,[UNIT]     = UPPER(RTRIM(LTRIM([UNIT])))
        ,[ATTENDING]= UPPER(RTRIM(LTRIM([ATTENDING])))
        ,[SERVICE]  = UPPER(RTRIM(LTRIM([SERVICE])))
        ,[ADX]      = UPPER(RTRIM(LTRIM([ADX])));
    
-- group the imported table data (the data may have duplicate rows)

;with cte as 
(
    SELECT  [ABX_NAME]
            ,[ROUTE]
            ,[DELIVERY_TIME]
            ,CAST([HOSPNO] AS INT) AS [HOSPNO]
            ,[PTNAME]
            ,[UNIT]
            ,[ATTENDING]
            ,[SERVICE]
            ,[ADX]
    FROM    [Essentris].[dbo].[IMPORTED_Vanco]
    GROUP BY [ABX_NAME]
            ,[ROUTE]
            ,[DELIVERY_TIME]
            ,CAST([HOSPNO] AS INT)
            ,[PTNAME]
            ,[UNIT]
            ,[ATTENDING]
            ,[SERVICE]
            ,[ADX]
)
-- and insert it into the temp table from the cte
insert into #tempvanco 
select * from cte;

-- Up to this point, the contents of #tempvanco is as expected

--================

-- merge #tempvanco into our concrete table. 

MERGE INTO [Essentris].[dbo].[VancoMycin] AS t
USING #tempVanco AS s
ON	
(
    -- this is really weird: if I just use the three fields below, it reinserts fewer 
    -- records than if I use ALL of the fields. This hints at there being a problem 
    -- with one or more of the string fields, but after they've been normalized this 
    -- theoretically should not be the case.
        t.[DELIVERY_TIME] = s.[DELIVERY_TIME]
    AND t.[HOSPNO]        = s.[HOSPNO]
    -- I tried using "like" instead of "=", and it had no effect
    AND	t.[PTNAME]        like s.[PTNAME]
    --and t.[ABX_NAME]      = s.[ABX_NAME]
    --and t.[ROUTE]         = s.[ROUTE]
    --and t.[UNIT]          = s.[UNIT]
    --and t.[ATTENDING]     = s.[ATTENDING]
    --and t.[SERVICE]       = s.[SERVICE]
    --and t.[ADX]           = s.[ADX]
)

-- We should never need to update anything, so we only react when a record is new

WHEN NOT MATCHED BY TARGET THEN
    INSERT 
    (
        [ABX_NAME]
        ,[ROUTE]
        ,[DELIVERY_TIME]
        ,[HOSPNO]
        ,[PTNAME]
        ,[UNIT]
        ,[ATTENDING]
        ,[SERVICE]
        ,[ADX]

        -- we need to create some data to fill in these fields
        ,[ABX_NAME_SHORT]
        ,[DELIVERY_DATE]
        ,InsertOrdinal
    )
    VALUES
    (
        s.[ABX_NAME]
        ,s.[ROUTE]
        ,s.[DELIVERY_TIME]
        ,s.[HOSPNO]
        ,s.[PTNAME]
        ,s.[UNIT]
        ,s.[ATTENDING]
        ,s.[SERVICE]
        ,s.[ADX]

        -- created data
        ,'VANCOMYCIN'
        ,CONVERT(DATE, s.[DELIVERY_TIME])
        ,@nextOrdinal
    );

-- drop the temporary table

IF OBJECT_ID('tempdb..#tempVanco') IS NOT NULL 
BEGIN
    DROP TABLE #tempVanco;
END
Posted
Updated 1-Sep-17 2:04am
v5
Comments
Richard Deeming 31-Aug-17 14:27pm    
Since you're only inserting records, you could get rid of the MERGE:
INSERT INTO [Essentris].[dbo].[VancoMycin]
(
    ...
)
SELECT
    ...
FROM
    #tempVanco As S
WHERE
    Not Exists
    (
        SELECT 1
        FROM [Essentris].[dbo].[VancoMycin] As T
        WHERE t.[DELIVERY_TIME] = s.[DELIVERY_TIME]
        AND t.[HOSPNO] = s.[HOSPNO]
        AND t.[PTNAME] = s.[PTNAME]
        ...
    )
;

You could also try running the SELECT without the INSERT, to see if there's anything obviously wrong.
#realJSOP 31-Aug-17 14:52pm    
I get the same result.

On run #1, the target table is empty, and the (grouped) source table is essentially copied over in its entirety (42000 records).

On run #2, processing the SAME (grouped) source table should yield 0 inserts, but I get 139.

WTF?
Richard Deeming 31-Aug-17 15:03pm    
Could it be the datetime column? The old datetime type is only precise to 3.33 ms, so maybe there's a difference creeping in between the tables?
#realJSOP 1-Sep-17 6:51am    
Both tables were created with the same version of SQL.
#realJSOP 1-Sep-17 7:21am    
I tried converting everything to a varchar(255) in the comparison, and it still can't match the same handful of records.

This has got to be one of the single most bizarre problems I've ever encountered in sql...


1 solution

I finally got it to work by creating a view that grouped the records, and using that view in the merge statement.

I still don't know what was causing sql server to puke on the temp table...

------------------------

Update: It appears to be a bug in SQL Server 2008R2. I tried it at home on SQL Express 2012, and the problem doesn't appear to affect that version.
 
Share this answer
 
v2
Comments
Dave Kreskowiak 1-Sep-17 8:29am    
This might be an issue to bring up with MS. The question I have is this a subtle bug of some kind and does the same problem occur in later versions of SQL Server.
#realJSOP 1-Sep-17 10:25am    
Well, since 2008R2 has been superseded 4(?) times, it's probably not worth bringing up, because MS will just ignore me because the report probably has to apply to whatever version of crapware they might be selling at the time. In the mean time, I have to go back and re-evaluate ALL of my stored procs that use the merge statement (I think there are a little more than 100 of them).

The DBAs are going to "upgrade" our server to 2012 "one of these days", and at that point, if I'm not buried in other minutia (and can summon enough interest), I'll see if the same thing happens.
Dave Kreskowiak 1-Sep-17 11:57am    
Not that I've ever used an SQL MERGE before, it just sounds like an interesting problem. I hope MS has it fixed by now.

We're just starting to get off of 2012. All new servers are being built 2014 and they just stood up a few 2016 to test apps on.
#realJSOP 2-Sep-17 14:04pm    
The problem appears not to exist in 2012.
Dave Kreskowiak 2-Sep-17 14:19pm    
Well then. Problem solved. Just upgrade all the servers to 2014! :)

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900