0

我有一个合并声明

目标表 =~ DST 源表 =~ SRC

MERGE DST
USING (SELECT * FROM SRC WHERE <Some_Condition>) SRC
ON SCR.COL1 = DST.COL1 OR (SRC.COL1 IS NULL AND DST.COl1 IS NULL) AND 
ON SCR.COL2 = DST.COL2 OR (SRC.COL2 IS NULL AND DST.COl2 IS NULL) AND 
ON SCR.COL3 = DST.COL3 OR (SRC.COL3 IS NULL AND DST.COl3 IS NULL) AND 
ON SCR.COL4 = DST.COL4 OR (SRC.COL4 IS NULL AND DST.COl4 IS NULL) 
WHEN MATCHED UPDATE DST
WHEN NOT MATCHED BY SOURCE THEN UPDATE DST
WHEN NOT MATCHED INSERT IN DST

表结构

Source

Column1 Column2 Column3 Column4
A        A       NULL    NULL
B        B       NULL    NULL

Destination

Column1 Column2 COlumn3 Column4 Column5

错误

MERGE 语句多次尝试更新或删除同一行。当目标行匹配多个源行时会发生这种情况。MERGE 语句不能多次 UPDATE/DELETE 目标表的同一行。优化 ON 子句以确保目标行最多匹配一个源行,或使用 GROUP BY 子句对源行进行分组。

但是当我使用下面的查询时(即只使用那些有一些值而不是空值的列,如 SRC 中的 Column3 和 Column 4)一切正常

MERGE DST
    USING (SELECT * FROM SRC WHERE <Some_Condition>) SRC
    ON SCR.COL1 = DST.COL1 OR (SRC.COL1 IS NULL AND DST.COl1 IS NULL) AND 
    ON SCR.COL2 = DST.COL2 OR (SRC.COL2 IS NULL AND DST.COl2 IS NULL) 
    WHEN MATCHED UPDATE DST
    WHEN NOT MATCHED BY SOURCE THEN UPDATE DST
    WHEN NOT MATCHED INSERT IN DST

编辑:复制场景

源表

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO
CREATE TABLE [dbo].[tblSource](
    [Column1] [varchar](50) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
    [Column2] [varchar](50) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
    [Column3] [varchar](50) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
    [Column4] [varchar](50) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
    [Column5] [varchar](50) COLLATE SQL_Latin1_General_CP1_CI_AS NULL
) ON [PRIMARY]

GO
SET ANSI_PADDING OFF

目的地表

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO
CREATE TABLE [dbo].[tblDestination](
    [Column1] [varchar](50) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
    [Column2] [varchar](50) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
    [Column3] [varchar](50) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
    [Column4] [varchar](50) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
    [Column5] [varchar](50) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
    [Column6] [varchar](50) COLLATE SQL_Latin1_General_CP1_CI_AS NULL
) ON [PRIMARY]

GO
SET ANSI_PADDING OFF

数据来源

INSERT [dbo].[tblSource] ([Column1], [Column2], [Column3], [Column4], [Column5]) VALUES (N'A', N'A', NULL, NULL, NULL)
INSERT [dbo].[tblSource] ([Column1], [Column2], [Column3], [Column4], [Column5]) VALUES (N'B', N'B', NULL, NULL, NULL)

目的地为空

合并声明

MERGE dbo.tblDestination DST
USING (SELECT * FROM dbo.tblSource) SRC
ON DST.Column1 = SRC.Column1 OR (DST.Column1 IS NULL AND SRC.Column1 IS NULL) AND
DST.Column2 = SRC.Column2 OR (DST.Column2 IS NULL AND SRC.Column2 IS NULL) AND
DST.Column3 = SRC.Column3 OR (DST.Column3 IS NULL AND SRC.Column3 IS NULL) AND
DST.Column4 = SRC.Column4 OR (DST.Column4 IS NULL AND SRC.Column4 IS NULL) AND
DST.Column5 = SRC.Column5 OR (DST.Column5 IS NULL AND SRC.Column5 IS NULL)

WHEN MATCHED THEN
        UPDATE SET COLUMN5 = 'A'
WHEN NOT MATCHED BY SOURCE THEN
        UPDATE SET Column5 = 'B'
WHEN NOT MATCHED THEN
        INSERT (Column1, Column2, Column3, Column4, Column5)
        VALUES (Column1, Column2, Column3, Column4, Column5) ;

运行它两次以查看错误。

现在截断目标表

现在相同的查询,但只有那些有数据的列

MERGE dbo.tblDestination DST
USING (SELECT * FROM dbo.tblSource) SRC
ON DST.Column1 = SRC.Column1 OR (DST.Column1 IS NULL AND SRC.Column1 IS NULL) AND
DST.Column2 = SRC.Column2 OR (DST.Column2 IS NULL AND SRC.Column2 IS NULL) 


WHEN MATCHED THEN
        UPDATE SET COLUMN5 = 'A'
WHEN NOT MATCHED BY SOURCE THEN
        UPDATE SET Column5 = 'B'
WHEN NOT MATCHED THEN
        INSERT (Column1, Column2, Column3, Column4, Column5)
        VALUES (Column1, Column2, Column3, Column4, Column5) ;

这工作得非常好,您可以多次运行它。

4

1 回答 1

2

这是Operator Precedence的问题。

DST.Column1 = SRC.Column1 OR (DST.Column1 IS NULL AND SRC.Column1 IS NULL) ....将匹配所有行,DST.Column1 = SRC.Column1无论其他列中的值如何。

您需要在 OR:ed 条件周围添加一些括号以覆盖默认优先级。

MERGE dbo.tblDestination DST
USING (SELECT * FROM dbo.tblSource) SRC
ON (DST.Column1 = SRC.Column1 OR (DST.Column1 IS NULL AND SRC.Column1 IS NULL)) AND
   (DST.Column2 = SRC.Column2 OR (DST.Column2 IS NULL AND SRC.Column2 IS NULL)) AND
   (DST.Column3 = SRC.Column3 OR (DST.Column3 IS NULL AND SRC.Column3 IS NULL)) AND
   (DST.Column4 = SRC.Column4 OR (DST.Column4 IS NULL AND SRC.Column4 IS NULL)) AND
   (DST.Column5 = SRC.Column5 OR (DST.Column5 IS NULL AND SRC.Column5 IS NULL))

WHEN MATCHED THEN
        UPDATE SET COLUMN5 = 'A'
WHEN NOT MATCHED BY SOURCE THEN
        UPDATE SET Column5 = 'B'
WHEN NOT MATCHED THEN
        INSERT (Column1, Column2, Column3, Column4, Column5)
        VALUES (Column1, Column2, Column3, Column4, Column5) ;
于 2013-01-13T18:18:24.217 回答