Sql server的Merge語句,源表中如果有重復數據會導致執行報錯


用過sql server的Merge語句的開發人員都應該很清楚Merge用來做表數據的插入/更新是非常方便的,但是其中有一個問題值得關注,那就是Merge語句中的源表中不能出現重復的數據,我們舉例來說明這個問題。

 

現在我們有一張表叫T_Class_A,其建表語句如下:

CREATE TABLE [dbo].[T_Class_A](
    [ID] [int] IDENTITY(1,1) NOT NULL,
    [ClassName] [nvarchar](50) NULL,
    [StudentTotalCount] [int] NULL,
    [Owner] [nvarchar](50) NULL,
 CONSTRAINT [PK_T_Class_A] PRIMARY KEY CLUSTERED 
(
    [ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

GO

插入數據的腳本如下:

SET IDENTITY_INSERT [dbo].[T_Class_A] ON 

GO
INSERT [dbo].[T_Class_A] ([ID], [ClassName], [StudentTotalCount], [Owner]) VALUES (1, N'Class 1', 35, N'Jim')
GO
INSERT [dbo].[T_Class_A] ([ID], [ClassName], [StudentTotalCount], [Owner]) VALUES (2, N'Class 2', 36, N'Bob')
GO
INSERT [dbo].[T_Class_A] ([ID], [ClassName], [StudentTotalCount], [Owner]) VALUES (3, N'Class 3', 51, N'James')
GO
INSERT [dbo].[T_Class_A] ([ID], [ClassName], [StudentTotalCount], [Owner]) VALUES (4, N'Class 4', 45, N'Rose')
GO
INSERT [dbo].[T_Class_A] ([ID], [ClassName], [StudentTotalCount], [Owner]) VALUES (5, N'Class 5', 43, N'Tom')
GO
INSERT [dbo].[T_Class_A] ([ID], [ClassName], [StudentTotalCount], [Owner]) VALUES (6, N'Class 6', 30, N'Clark')GO
SET IDENTITY_INSERT [dbo].[T_Class_A] OFF
GO

執行上面兩段SQL腳本之后,表T_Class_A的數據如下所示:

 

現在我們有另外一張表T_Class_B,其結構和T_Class_A完全一樣,我們要使用Merge語句用T_Class_A的數據來構造表T_Class_B的數據(相同的ClassName就Update,否者就Insert)。T_Class_B的建表語句如下:

CREATE TABLE [dbo].[T_Class_B](
    [ID] [int] IDENTITY(1,1) NOT NULL,
    [ClassName] [nvarchar](50) NULL,
    [StudentTotalCount] [int] NULL,
    [Owner] [nvarchar](50) NULL,
 CONSTRAINT [PK_T_Class_B] PRIMARY KEY CLUSTERED 
(
    [ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

 

接下來我們執行如下Merge語句把T_Class_A表的數據插入到T_Class_B表中去:

merge into [dbo].[T_Class_B]
using [dbo].[T_Class_A] -- 這里的[dbo].[T_Class_A]也可以是子查詢
on [T_Class_A].[ClassName]=[T_Class_B].[ClassName]
when matched 
then update  set [T_Class_B].[StudentTotalCount]=[T_Class_A].[StudentTotalCount],[T_Class_B].[Owner]=[T_Class_A].[Owner]
when not matched
then insert([ClassName],[StudentTotalCount],[Owner]) values([T_Class_A].[ClassName],[T_Class_A].[StudentTotalCount],[T_Class_A].[Owner]);

之后我們可以看到T_Class_B表中的數據和T_Class_A表完全一樣了:

 

現在我們更改T_Class_A表的數據,將Owner全部改為Unknown,如下語句所示:

update T_Class_A set [Owner]=N'Unknown'

然后再執行上面的Merge語句:

merge into [dbo].[T_Class_B]
using [dbo].[T_Class_A] -- 這里的[dbo].[T_Class_A]也可以是子查詢
on [T_Class_A].[ClassName]=[T_Class_B].[ClassName]
when matched 
then update  set [T_Class_B].[StudentTotalCount]=[T_Class_A].[StudentTotalCount],[T_Class_B].[Owner]=[T_Class_A].[Owner]
when not matched
then insert([ClassName],[StudentTotalCount],[Owner]) values([T_Class_A].[ClassName],[T_Class_A].[StudentTotalCount],[T_Class_A].[Owner]);

然后查看T_Class_B表中的數據如下,可以看到T_Class_B表的Owner字段都被Merge語句Update為了"Unknown"了:

 

很好到現在為止我們的Merge語句都工作得很不錯,沒有出現問題。接下來我們在T_Class_A表中再插入一條數據,如下語句所示:

INSERT [dbo].[T_Class_A] ([ClassName], [StudentTotalCount], [Owner]) VALUES (N'Class 6', 38, N'Terry')

此時我們查看T_Class_A表中的數據如下:

我們發現此時,T_Class_A表中有兩行ClassName為"Class 6"的數據行,那么現在我們再執行上面的Merge語句,如下所示:

merge into [dbo].[T_Class_B] using [dbo].[T_Class_A] -- 這里的[dbo].[T_Class_A]也可以是子查詢 on [T_Class_A].[ClassName]=[T_Class_B].[ClassName] when matched then update set [T_Class_B].[StudentTotalCount]=[T_Class_A].[StudentTotalCount],[T_Class_B].[Owner]=[T_Class_A].[Owner] when not matched then insert([ClassName],[StudentTotalCount],[Owner]) values([T_Class_A].[ClassName],[T_Class_A].[StudentTotalCount],[T_Class_A].[Owner]);

結果現在我們發現Sql server在執行Merge語句的時候報錯了,錯誤如下所示:

消息 8672,級別 16,狀態 1,第 1 行
The MERGE statement attempted to UPDATE or DELETE the same row more than once. This happens when a target row matches more than one source row. A MERGE statement cannot UPDATE/DELETE the same row of the target table multiple times. Refine the ON clause to ensure a target row matches at most one source row, or use the GROUP BY clause to group the source rows.

原因上面的錯誤消息也寫的很清楚了,就是因為現在Merge語句的源表T_Class_A中有兩行ClassName為"Class 6"的數據,那么這會導致Merge語句中目標表T_Class_B中ClassName為"Class 6"的這一行數據Match兩次T_Class_A表中的數據,而這在Merge語句中是不允許的,Merge語句只允許目標表T_Class_B中的每行數據最多被源表T_Class_A中的數據Match一次。這就是為什么這里Merge語句會報錯的原因。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM