【问题标题】:TSQL merge: target table where on "ON" conditionTSQL 合并:在“ON”条件下的目标表
【发布时间】:2019-01-04 12:15:35
【问题描述】:

我想使用merge,但被来自reference 的关于ON 连接条件的警告吓倒了:

重要的是只指定目标表中的列 用于匹配目的。也就是说,从 与相应列进行比较的目标表 源表。不要试图通过过滤来提高查询性能 在 ON 子句中取出目标表中的行,例如通过指定 AND NOT target_table.column_x = 值。这样做可能会意外返回 和错误的结果。

这是一个例子:

-- drop table trg
create table trg(department int not null,student int not null,name nvarchar(20))
alter table trg add constraint PK_trg primary key clustered (department,student)
insert trg values (12,0,'Tony'),(12,1,'Helen'),(55,0,'Tony'),(55,1,'Helen')

-- drop table src 
go
create table src(student int not null,name nvarchar(20)) 
go
alter table src add constraint PK_src primary key clustered (student) 
go
insert src values (0,'Antony'),(1,'Helen'),(2,'Mike')

select * from trg
select * from src

trg 表

+------------+---------+-------+
| department | student | name  |
+------------+---------+-------+
|         12 |       0 | Tony  |
|         12 |       1 | Helen |
|         55 |       0 | Tony  |
|         55 |       1 | Helen |
+------------+---------+-------+

两个部门有2个学生(请忽略2nf违规,第三列也应该依赖于部门,但我现在想不出例子)。

现在,我们得到了一个src 表,该表有关于部门 12 的信息:

+---------+--------+
| student |  name  |
+---------+--------+
|       0 | Antony |
|       1 | Helen  |
|       2 | Mike   |
+---------+--------+

...我们想在src 中用merge 插入此信息。

使用这个:

merge trg using src on trg.student=src.student and trg.department=12
    when matched then update set name=src.name
    when not matched by target then insert values (12,src.student,src.name)
;

做我们想要的。 trg 表现在具有所需的输出:

+------------+---------+--------+
| department | student |  name  |
+------------+---------+--------+
|         12 |       0 | Antony |
|         12 |       1 | Helen  |
|         12 |       2 | Mike   |
|         55 |       0 | Tony   |
|         55 |       1 | Helen  |
+------------+---------+--------+

我们可以看到,12 部门 Tony 已将名称更改为 Antony,Mike 被插入到部门 12,没有其他任何事情发生。这是由违反参考警告造成的。可以吗?

我想它可以重写为:

merge trg using src on trg.student=src.student
    when matched and trg.department=12 then update set name=src.name
    when not matched by target then insert values (12,src.student,src.name);

这确实也可以正常工作。

第一种与警告相矛盾的方法是错误的,还是不好的做法?为什么?

【问题讨论】:

  • 您好,请问...安东尼、海伦和迈克是 12 系的新生吗?如果是这样,则问题不是参考,因为您的关系是“学生”列,即...学生 0 = Tony -> Mike, Student 1 = Helen -> Helen, 2 = NULL -> Mike (new row ),因此,在组合条件中也将包括名称...问候

标签: sql-server tsql merge


【解决方案1】:

警告的原因是,如果您的源表包含类似 55,0,Tony 的内容,那么这将被视为不匹配并转到可能意外的 INSERT 分支。

但是,如果您的源表保证只包含部门 12 的项目,那么这将按您的意愿工作。

在这种情况下,您还可以使用诸如 CTE 之类的表表达式

WITH trg12
     AS (SELECT *
         FROM   trg
         WHERE  department = 12)
MERGE trg12 trg
using src
ON trg.student = src.student
WHEN matched THEN
  UPDATE SET name = src.name
WHEN NOT matched BY target THEN
  INSERT
  VALUES (12,
          src.student,
          src.name);
;

【讨论】:

  • 谢谢。这将是一个有用的参考。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-01-09
  • 1970-01-01
  • 2020-06-10
  • 1970-01-01
  • 2011-11-21
相关资源
最近更新 更多