TSQL merge: целевая таблица в состоянии «ON» - PullRequest
0 голосов
/ 04 января 2019

Я хочу использовать merge, и меня пугает это предупреждение от ссылки относительно условия соединения ON:

Важно указывать только столбцы из целевой таблицы, которые используются для соответствия целей. То есть укажите столбцы из целевой таблицы, которые сравниваются с соответствующим столбцом исходная таблица. Не пытайтесь улучшить производительность запросов с помощью фильтрации строк в целевой таблице в предложении ON, например, указав И НЕ 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

Таблица ТРГ

+------------+---------+-------+
| 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 Тони изменили имя на Антоний, в Майк добавили отдел 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);

Что действительно также работает правильно.

Является ли первый способ, который противоречит предупреждению, неправильным или плохой практикой? Почему?

1 Ответ

0 голосов
/ 04 января 2019

Причина предупреждения заключается в том, что если ваша исходная таблица содержит что-то вроде 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);
;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...