【发布时间】:2013-01-11 09:31:33
【问题描述】:
我有一个合并语句
目的地表 =~ 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) ;
这运行得非常好,您可以多次运行它。
【问题讨论】:
-
为了更快地得到这个问题的答案,我认为您应该花一些时间添加一些示例代码,以实际显示您遇到的问题。您在此处发布的代码中有很多错误,因此很难弄清楚您的问题到底是什么。
-
@Mikael Eriksson 我已经用代码和步骤更新了我的问题以复制场景。希望这会有所帮助。
-
您的源样本包含两行完全相同的数据。我猜它们都是第一次插入的,但是如果目标包含匹配的行,它将匹配两个源行,你会遇到同样的问题。你的真实数据是这样的吗?是否没有一个或多个键列可以让您将每个目标行与不超过一个源行进行匹配?
-
您解决了这个问题还是还有问题?
-
使用适当的括号来解决 Mikael 下面提到的问题。这是唯一的问题。
标签: sql-server-2008 join merge sql-server-2008-r2