#realJSOP Ответов: 1

Оператор слияния 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 году.

1 Ответов

Рейтинг:
10

#realJSOP

В конце концов я заставил его работать, создав представление, сгруппировавшее записи, и используя это представление в операторе merge.

Я до сих пор не знаю, что заставило sql server блевать на временную таблицу...

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

Обновление: похоже, это ошибка в SQL Server 2008R2. Я попробовал его дома на SQL Express 2012, и проблема, похоже, не влияет на эту версию.


Dave Kreskowiak

Это может быть проблемой, которую нужно обсудить с Мисс. Вопрос, который у меня есть, заключается в том, что это какая-то тонкая ошибка и возникает ли та же проблема в более поздних версиях SQL Server.

#realJSOP

Ну, с тех пор как 2008R2 был заменен 4(?) раза, это, вероятно, не стоит поднимать, потому что MS просто проигнорирует меня, потому что отчет, вероятно, должен относиться к любой версии crapware, которую они могли бы продавать в то время. В то же время я должен вернуться назад и заново оценить все мои сохраненные процессы, которые используют оператор merge (я думаю, что их немного больше 100).

DBAs собираются " обновить "наш сервер до 2012 года" на днях", и в этот момент, если я не похоронен в других мелочах (и могу вызвать достаточный интерес), я посмотрю, произойдет ли то же самое.

Dave Kreskowiak

Не то чтобы я когда-либо раньше использовал слияние SQL, просто это звучит как интересная проблема. Надеюсь, Мисс уже все исправила.

Мы только начинаем выходить из 2012 года. Все новые серверы строятся в 2014 году, и они просто встали несколько раз в 2016 году, чтобы протестировать приложения.

#realJSOP

В 2012 году этой проблемы, по-видимому, не существует.

Dave Kreskowiak

Ну тогда. Проблема решена. Просто обновите все серверы до 2014 года! :)