【问题标题】:Understanding SQL Merge statement?了解 SQL Merge 语句?
【发布时间】:2019-08-07 14:04:12
【问题描述】:

我有一个源表,其数据与我的目标表相同。当我尝试运行合并语句时,它失败并出现错误

合并不能多次更新目标行。

所以我的问题是,因为它们与 SQL 成功的原因相同,但受影响的是 0 行。请帮助我理解这一点。

顺便说一句,我的语法是正确的,因为在我最初的插入中它成功了,问题是如果再次重新运行它。

谢谢。

目标表和源表有相同的数据。

WHEN MATCHED AND ISNULL(T.VALUE,'') <> ISNULL(S.VALUE,'')

COL1          COL2    COL3   VALUE      DATE
1              A       TYPE    3       2019-01-02
2              B       KIND    4       2019-01-03
1              A       COLOR   0       2019-01-02
2              B       KIND    0       2019-01-03

MERGE TargetTable T
    USING
        (
            SELECT   COL1,
                     COL2,
                     COL3,
                     VALUE,
                     DATE    
            FROM SourceTable S
        ) s
        ON
        (                   
                    S.COL1  = T.COL1
                AND S.COL2  = T.COL2
                AND S.COL3  = T.COL3
                AND S.DATE  = T.DATE                                    
        )

    WHEN MATCHED AND
        (                   
            ISNULL(S.VALUE,'')  <> ISNULL(T.VALUE,'')
        )
    THEN UPDATE
        SET                             
            T.VALUE  = S.VALUE                          

    WHEN NOT MATCHED
    THEN INSERT VALUES
        (                   
             S.COL1 
            ,S.COL2 
            ,S.COL3
            ,S.VALUE    
            ,S.DATE
        );

【问题讨论】:

  • 合并语句在哪里?
  • 您应该标记目标数据库引擎。是 SQL Server 吗?
  • 很抱歉没有意识到这一点,是的,它是 SQL 2017

标签: sql-server sql-server-2017 sql-merge


【解决方案1】:

为了更好地理解合并:

MERGE 是一种 DML 语句(数据操作语言)。 也称为 UPSERT(更新插入)。 它尝试根据您定义的条件将源(表/视图/查询)与目标(表/可更新视图)匹配,然后根据匹配结果将行插入/更新/删除目标表/中/目标表。 MERGE (Transact-SQL)

create table src (i int, j int);
create table trg (i int, j int);

insert into src values (1,1),(2,2),(3,3);
insert into trg values (2,20),(3,30),(4,40);

merge into  trg
using       src
on          src.i = trg.i
when not matched by target then insert (i,j) values (src.i,src.j)
when not matched by source then update set trg.j = -1
when matched then update set trg.j = trg.j + src.j
;

select * from trg order by i

+---+----+
| i | j  |
+---+----+
| 1 | 1  |
+---+----+
| 2 | 22 |
+---+----+
| 3 | 33 |
+---+----+
| 4 | -1 |
+---+----+

来源: Stackoverflow SQL Merge

【讨论】:

  • 我认为 OP 了解合并的基础知识。但这无助于解释错误。
【解决方案2】:

我无法重现错误,但发现了一些有趣的东西

SQL DEMO

正如您提到的第一次合并运行完美,但在我的情况下,第二次合并说更新 2 行。

所以我修改了第二次合并以检测更新了哪些行。

WHEN MATCHED AND
    (                   
        ISNULL(S.VALUE,'')  <> ISNULL(T.VALUE,'')
    )
THEN UPDATE
 SET T.VALUE  = S.VALUE + 10

输出

+------+------+-------+-------+---------------------+
| COL1 | COL2 | COL3  | VALUE |        DATE         |
+------+------+-------+-------+---------------------+
|    1 | A    | TYPE  |     3 | 02/01/2019 00:00:00 |
|    2 | B    | KIND  |    10 | 03/01/2019 00:00:00 |
|    1 | A    | COLOR |     0 | 02/01/2019 00:00:00 |
|    2 | B    | KIND  |    14 | 03/01/2019 00:00:00 |
+------+------+-------+-------+---------------------+

因为您有 2 行与 (COL1, COL2, COL3, DATE) 完全匹配,系统会告诉您不知道哪一行更新了哪一行。

但这并不能解释为什么在我的演示中按预期工作。

所以我的建议是你必须在你的表中添加一个 PK 以确保合并发生在正确的行上。

【讨论】:

  • 谢谢,但是 +10 对值有什么作用,如果我愿意,可以是任何其他数字?我无法添加在这种情况下不可能的 PK。
  • 正如我解释的那样,我使用 +10 来检测哪些行正在更新,即使它们是相同的值。你有 2 行具有相同的组合 (COL1, COL2, COL3, DATE) 让我们调用那些 A1 的值 0A2 的值 4 所以当目标 A1 与源 A1 匹配时,没有任何变化,因为值是相同的。但是当 Target A1 与 Source A2 匹配时,值会发生变化。 Target A2 也是如此
  • 好的,所以您使用 +10 只是为了检测更新字段。我本可以使用 +4,它应该可以正常工作吧?
  • Juan,我试过了,你的 Demo,你通过加 10 来改变那里的值。如果不是你通过加 +10 来改变值,那么这不会起作用吗?这是我的问题,为什么?
  • 是的。只是为了调试建议所以可以看到发生了什么。如果您删除 sql 调试 上的 +10,您会看到有 2 行受到影响。您将获得预期的输出。但在这种情况下,0 变为 4,4 变为 0。我认为这是一种奇怪的行为,但不是您报告的错误。但我可以看到它们是如何相关的。也许是我平台上的设置而不是你的设置,不要将其检测为错误。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-01-26
  • 2010-11-28
  • 2011-02-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多