MERGE попытался ОБНОВИТЬ или УДАЛИТЬ одну и ту же строку более одного раза

Я пытаюсь загрузить стандартное измерение Kimball SCD2, используя оператор слияния, который я получил со следующего веб-сайта: http://www.kimballgroup.com/2008/11/design-tip-107-использование-оператора-sql-слияния-для-медленно-изменяющейся-обработки-измерения/

Этот оператор слияния такой же, за исключением обработки новых сущностей. Это будет обработано как прямая вставка в поток данных. Эта проблема касается только нескольких версий одного и того же бизнес-ключа.

Когда я выполняю оператор слияния, SQL возвращает ошибку:

Сообщение 8672, уровень 16, состояние 1, строка 3
Оператор MERGE попытался выполнить ОБНОВЛЕНИЕ или УДАЛЕНИЕ одной и той же строки более одного раза. Это происходит, когда целевая строка соответствует более чем одной исходной строке.
Операция MERGE не может ОБНОВЛЯТЬ/УДАЛИТЬ одну и ту же строку целевой таблицы несколько раз. Уточните предложение ON, чтобы целевая строка соответствовала не более чем одной исходной строке, или используйте предложение GROUP BY, чтобы сгруппировать исходные строки.

Я использую SQL Server 2012:

ИСХОДНЫЙ НАБОР ДАННЫХ

введите здесь описание изображения

ЦЕЛЕВОЙ НАБОР ДАННЫХ

введите здесь описание изображения

Это то, что я ожидал:

введите здесь описание изображения

Ниже вы можете найти скрипт для воспроизведения проблемы:

CREATE TABLE SANDBOX.EHN.SOURCE_SCD2 (
  BUSINESS_KEY  BIGINT
 ,DESCRIPTION_A VARCHAR(2)
 ,M_CRC         BIGINT
 ,StartDATE     DATE
 ,EndDATE       DATE )



CREATE TABLE SANDBOX.EHN.TARGET_SCD2 (
  BUSINESS_KEY  BIGINT
 ,DESCRIPTION_A VARCHAR(2)
 ,M_CRC         BIGINT
 ,StartDATE     DATE
 ,EndDATE       DATE )



 select *
 from SANDBOX.EHN.TARGET_SCD2

 truncate table SANDBOX.EHN.TARGET_SCD2

 INSERT INTO SANDBOX.EHN.SOURCE_SCD2 VALUES (1, 'B',  1,   '2015-05-16', '2015-06-01')
 INSERT INTO SANDBOX.EHN.SOURCE_SCD2 VALUES (1, 'C',  2,   '2015-06-01', '2015-06-11')
 INSERT INTO SANDBOX.EHN.SOURCE_SCD2 VALUES (1, 'D',  3,   '2015-06-11', '9999-12-31')

 INSERT INTO SANDBOX.EHN.TARGET_SCD2 VALUES (1,  'A', 0,    '2015-01-16', '9999-12-31')



INSERT INTO SANDBOX.EHN.TARGET_SCD2 
     SELECT BUSINESS_KEY
          ,DESCRIPTION_A
         ,M_CRC
         ,StartDATE
         ,EndDATE
FROM (
MERGE SANDBOX.EHN.TARGET_SCD2 D
USING SANDBOX.EHN.SOURCE_SCD2 UPD
ON(D.BUSINESS_KEY = UPD.BUSINESS_KEY )
    WHEN MATCHED AND D.EndDATE = '9999-12-31' 
    THEN UPDATE SET  D.EndDATE =  UPD.EndDATE
OUTPUT $Action Action_Out, UPD.BUSINESS_KEY
                         , UPD.DESCRIPTION_A
                    , UPD.M_CRC
                    , UPD.StartDATE
                    , UPD.EndDATE
)AS MERGE_OUT
WHERE MERGE_OUT.Action_Out = 'UPDATE'

Можете ли вы помочь мне решить эту проблему?


person Erik hoeven    schedule 18.11.2016    source источник
comment
Что ж, сообщение об ошибке довольно ясное: у вас есть целевая строка, которую оператор слияния пытался обновить или удалить более одного раза. Это не допускается для оператора слияния. Перепишите свой запрос, чтобы убедиться, что строка может быть целью только одного оператора обновления или удаления.   -  person TT.    schedule 21.11.2016
comment
Спасибо за ответ, но именно в этом проблема, мы знаем, «что» нам нужно делать, но не знаем, «как»?   -  person Erik hoeven    schedule 21.11.2016
comment
Я немного озадачен... AFAICT оператор MERGE является оператором верхнего уровня и не может использоваться внутри производной таблицы, как вы делаете. По крайней мере, я не вижу такого возможного использования в документации. . Я выполнил ваш сценарий в тестовой базе данных (SS 2012), и он выдает сообщение об ошибке, которое, я думаю, связано с этим фактом (Неправильный синтаксис рядом с «D» в начале производной таблицы, содержащей слияние ). Я что-то упускаю?   -  person TT.    schedule 21.11.2016
comment
MERGE переоценен для SCD. Я всегда использую отдельные INSERT/UPDATE в транзакции. Там больше кода, и он повторяется, но его легче отлаживать. Любопытная вещь в вашем обновлении SCD заключается в том, что источник также ограничен датой. Мало того, что к вам прибывают сразу трое (а не один, как обычно). Так что ваш случай не распространен, но достижим. Это не распространяется ни на одно из онлайн-предложений ванильного SCD. В любом случае, я могу увидеть SQL, который потребуется, и могу опубликовать, если хотите, или я могу объяснить более подробно.   -  person Nick.McDermaid    schedule 21.11.2016
comment
Ник, вы правы в том, что вы не часто обновляете один и тот же бизнес-ключ. Загружается раз в день. Потому что аппаратное обеспечение, на которое опирается dwh. Бывает, пропускаем день. К счастью, у нас есть озеро данных, в котором есть постоянная промежуточная область. У которого есть каждый из бизнес-ключей. Мне все еще любопытно ваше решение этой проблемы.   -  person Erik hoeven    schedule 21.11.2016
comment
Извините, у меня нет времени, чтобы дать более подробный ответ на ваше любопытство. Но в основном вы можете заменить слияние отдельными операторами обновления/вставки внутри транзакции. Вот статья, в которой перечислены некоторые проблемы со слиянием, хотя я не знаю, сколько из них все еще актуальны: mssqltips.com/sqlservertip/3074/   -  person Nick.McDermaid    schedule 23.11.2016
comment
Я также с @Nick.McDermaid здесь, у меня были проблемы с производительностью в прошлом (SQL server 2008R2/2012) с использованием Merge, и я вернулся к использованию надежного метода обновления/вставки, который намного лучше настраивается с точки зрения производительности IMO. Нашел ту же статью, что и у Ника, статус почти всех багов там тот же и если их изменить, то теперь пишет: Не исправить...   -  person Rogier Werschkull    schedule 23.11.2016
comment
Спасибо за все ваши комментарии. Я полностью согласен с вашими комментариями.   -  person Erik hoeven    schedule 23.11.2016


Ответы (1)


Только для последнего обновления используйте;

INSERT INTO SANDBOX.EHN.TARGET_SCD2 
     SELECT BUSINESS_KEY
          ,DESCRIPTION_A
         ,M_CRC
         ,StartDATE
         ,EndDATE
FROM (
MERGE SANDBOX.EHN.TARGET_SCD2 D
USING SANDBOX.EHN.SOURCE_SCD2 UPD
ON(D.BUSINESS_KEY = UPD.BUSINESS_KEY AND UPD.EndDATE = '9999-12-31')
    WHEN MATCHED AND D.EndDATE = '9999-12-31' 
    THEN UPDATE SET  D.EndDATE =  UPD.StartDATE
OUTPUT $Action Action_Out, UPD.BUSINESS_KEY
                         , UPD.DESCRIPTION_A
                         , UPD.M_CRC
                         , UPD.StartDATE
                         , UPD.EndDATE
)AS MERGE_OUT
WHERE MERGE_OUT.Action_Out = 'UPDATE'

Если вам нужны ВСЕ строки SRC в вашей целевой таблице, я согласен с Nick.McDermaid.

Для ВСЕХ строк используйте;

UPDATE TRG
  SET TRG.EndDate = SRC.StartDATE
FROM SANDBOX.EHN.TARGET_SCD2 TRG
JOIN  ( select SRC.BUSINESS_KEY, min(src.StartDATE)StartDATE
        from SANDBOX.EHN.SOURCE_SCD2 SRC
          group by SRC.BUSINESS_KEY
       )SRC
on  ( TRG.BUSINESS_KEY = SRC.BUSINESS_KEY
       AND SRC.StartDate > TRG.StartDate ) 
where 1 = 1


INSERT SANDBOX.EHN.TARGET_SCD2
SELECT * FROM SANDBOX.EHN.SOURCE_SCD2
person Kevin Rance    schedule 21.11.2016