Оператор слияния Sql server 2008R2 не всегда находит совпадение (даже если оно существует)
Я использую инструкцию SQL Server merge для обновления одной таблицы из другой (если запись в целевом объекте совпадает с записью в исходном, ничего не происходит, в противном случае выполняется вставка.
Теоретически, если я выполняю один и тот же запрос дважды подряд, во 2-м запуске запроса не должно быть никаких вставок, но я получаю несколько вставок, которые все еще выполняются. Понятия не имею почему.
Когда я сравниваю запись, которая уже существует, с записью, которую я пытаюсь вставить, значения Соединенных столбцов идентичны (что должно указывать на совпадение и, следовательно, отсутствие вставки), но оператор Merge все равно вставляет исходную запись.
Каждый раз, когда я выполняю инструкцию Merge, одна и та же горстка записей вставляется повторно.
Что я уже пробовал:
Я играл с этим кодом. Я обновил приведенный ниже код, чтобы показать его текущее состояние. Пожалуйста, ознакомьтесь с комментариями в блоке кода на предмет странности:
-- 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
Richard Deeming
Поскольку вы только вставляете записи, вы можете избавиться от 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] ... ) ;
Вы также можете попробовать запустить
SELECT
без этого INSERT
, чтобы увидеть, если есть что-то явно не так.
#realJSOP
Я получаю тот же результат.
При запуске №1 целевая таблица пуста, а (сгруппированная) исходная таблица по существу копируется полностью (42000 записей).
При запуске №2 обработка одной и той же (сгруппированной) исходной таблицы должна дать 0 вставок, но я получаю 139.
ВТФ?
Richard Deeming
Может ли это быть datetime
колонна? Старое datetime
тип является точным только до 3,33 мс, так что, может быть, между таблицами есть какая-то разница?
#realJSOP
Обе таблицы были созданы с одной и той же версией SQL.
#realJSOP
Я попытался преобразовать все в varchar(255) в сравнении, и он все еще не может соответствовать той же горстке записей.
Это, должно быть, одна из самых странных проблем, с которыми я когда-либо сталкивался в sql...
#realJSOP
Я просто попробовал его дома, используя SQL 2012 Express, со случайно сгенерированной базой данных из 54000 записей (примерно 11000 из которых были дубликатами записей), и он работал без создания представления, так что, думаю, они его исправили. по крайней мере, уже в 2012 году.