【问题标题】:Issues with SQL Server MERGE statementSQL Server MERGE 语句的问题
【发布时间】:2010-11-28 20:59:11
【问题描述】:

源表

Id, Name, Address
1   A     #202
1   A     #203
1   A     #204
2   A     #202

目标表

Id, Name, Address
1   A     NULL

合并后

Id, Name, Address
1   A     #202
2   A     #202

我正在使用这个 SQL

create table #S   (ID int, Name varchar(25) NULL, Address varchar(25) NULL)
create table #T   (ID int, Name varchar(25) NULL, Address varchar(25) NULL)

 INSERT #S values(1, 'A', '#202')
 INSERT #S values(1, 'A', '#203')
 INSERT #S values(1, 'A', '#203')
 INSERT #S values(1, 'A', '#204')

 INSERT #T values(1, 'A', NULL)

 MERGE #T USING
  (
Select id, name, address 
from #S
  ) AS S(id,name,address)
 on #T.id=S.id and #T.Name=S.Name
 when not matched THEN
    INSERT values(S.id,S.Name, S.Address)
 when matched then
    update set Address = S.Address;
 GO 

 Select * from #T
 GO 

 Select * from #S
 GO 

这会导致错误

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

我想用三个匹配值中的任何一个的地址值更新 A 中的行。如何做到这一点?

【问题讨论】:

    标签: sql sql-server tsql sql-server-2008 merge


    【解决方案1】:

    使用删除重复项

    select R.* 
    from  (SELECT Customer,Material,Received_date_time,
    row_number() over (Partition by Customer, Material   
    order by  Customer,Material,Received_date_time) as rn
          from Customer_Table WHERE Status=0     
         ) as R
    where R.rn = 1
    

    对于合并,您不能有重复项,因此您始终必须选择最新的

    【讨论】:

    • 视情况而定,这更有意义。也许你可以添加一个唯一的约束。
    【解决方案2】:

    #S 中的四个值中的任何一个都将匹配目标表的单行值(#S 中的所有值都有 id = 1 和 name = 'A' - 所以它们都匹配目标中的单行),因此这个值将被更新四次 - 这就是错误所说的,它是绝对正确的。

    你真正想在这里实现什么?

    是否要将地址设置为源表中的第一个值?在您的子选择中使用 TOP 1 子句:

    MERGE #T 
    USING (SELECT TOP 1 id, name, address FROM #S) AS S
    ON #T.id = S.id AND #T.Name = S.Name
    WHEN NOT MATCHED THEN
        INSERT VALUES(S.id,S.Name, S.Address)
    WHEN MATCHED THEN
        UPDATE SET Address = S.Address;
    

    是否要将地址设置为源表中值的随机元素?在您的子选择中使用TOP 1ORDER BY NEWID() 子句:

    MERGE #T 
    USING (SELECT TOP 1 id, name, address FROM #S ORDER BY NEWID()) AS S
    ON #T.id = S.id AND #T.Name = S.Name
    WHEN NOT MATCHED THEN
        INSERT VALUES(S.id,S.Name, S.Address)
    WHEN MATCHED THEN
        UPDATE SET Address = S.Address;
    

    如果您将四个源行与单个目标行匹配,您将永远不会得到有用的结果 - 您需要知道您真正想要什么。

    马克

    【讨论】:

    • 我想用三个匹配值中的任何一个的地址值更新 A 中的行
    • 但是你如何确定使用哪一个?
    • 我使用 Row_Number 既是源又是目标(视图)。然后匹配条件包括附加条件,如果行号与 (target.address isnull or (t.address = s.address)) 一起相等
    • @marc 我有同样的问题,但有额外的要求。我想更新来自源的最后一个值,并且我的源表还包括另一个字段 street,它的值将是 s1、s2、s3、s4。我想在更新完成 A s1、A s2、A s3、A s4 后得到这个结果,以便在另一个更新语句中使用这些数据。有什么想法吗?
    • 这不适用于所有情况。如果源具有需要移植到目标的审计日志信息,那么提取单行将如何工作?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-13
    • 1970-01-01
    相关资源
    最近更新 更多