Sql server MERGE多次尝试更新或删除同一行

Sql server MERGE多次尝试更新或删除同一行,sql-server,tsql,sql-merge,scd2,Sql Server,Tsql,Sql Merge,Scd2,我正在尝试使用从以下网站获得的合并语句加载标准KimballSCD2维度: 除了处理新实体外,此merge语句是相同的。这将作为数据流中的直接插入进行处理。此问题只涉及同一业务密钥的多个版本 执行merge语句时,SQL返回错误: 味精8672,第16级,状态1,第3行 MERGE语句多次尝试更新或删除同一行。当目标行与多个源行匹配时,会发生这种情况MERGE语句不能多次更新/删除目标表的同一行。优化ON子句以确保目标行最多匹配一个源行,或使用GROUP BY子句对源行进行分组 我正在使用SQ

我正在尝试使用从以下网站获得的合并语句加载标准KimballSCD2维度:

除了处理新实体外,此merge语句是相同的。这将作为数据流中的直接插入进行处理。此问题只涉及同一业务密钥的多个版本

执行merge语句时,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'

你能帮我解决这个问题吗?

上次更新仅限使用

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

错误消息相当清楚:您有一个目标行,merge语句多次尝试更新或删除该行。这对于合并语句是不允许的。重写查询以确保一行只能作为一个update或delete语句的目标。感谢您的答复,但这正是问题所在,我们知道需要做什么,但不知道如何做?我有点困惑。。。AFAICT MERGE语句是顶级语句,不能像您现在这样在派生表中使用。至少我没有看到这种可能的用途。我在一个测试数据库(SS 2012)上执行了您的脚本,它给出了一条错误消息,我认为这是由于这个事实(包含合并的派生表开头“D”附近的语法不正确)。我遗漏了什么吗?
MERGE
对SCD的评价过高。我总是在事务中使用单独的插入/更新。有更多的代码和重复,但更容易调试。SCD更新的奇怪之处在于,源代码也是以日期分隔的。不仅是三个同时到达(而不是通常情况下的一个)。因此,您的案例并不常见,但可以实现。这不包括在任何在线的普通SCD建议中。不管怎样,我可以看到所需的SQL,如果你愿意,我可以发布,或者我可以更详细地解释。对不起,我没有时间对你的好奇心做出更详细的回应。但基本上,您可以在事务中用单独的update/insert语句替换merge。这里有一篇文章列出了合并的一些问题,尽管我不知道有多少仍然有效: