СЛИЯНИЕ с T-SQL: возможен ли этот шаблон?: ВСТАВИТЬ (один раз), если флаг true или УДАЛИТЬ, если флаг false

#sql #sql-server #tsql #merge

#sql #sql-сервер #tsql #слияние

Вопрос:

Я хочу использовать один оператор SQL (т. Е. «ОБЪЕДИНИТЬ») для управления таблицей, представляющей математический набор: вставить элемент, если true, иначе удалить.

Используя мороженое в качестве примера, я хочу добавить вкус или удалить вкус из списка на основе флага @Exist ниже. Возможно ли это? Я играл с ним в течение нескольких часов, и либо он синтаксически корректен и не вставляет строку, либо я пишу запрос, чтобы сделать то, что, по моему мнению, нужно сделать, но затем не будет компилироваться, потому что синтаксис недопустим. (Страница «man» слияния MSDN)

Вот моя слабая попытка:

 CREATE TABLE IceCream(
    Flavor VARCHAR(50) NOT NULL,
    CONSTRAINT AK_Flavor UNIQUE(Flavor)
)

DECLARE @Flavor varchar(50)
DECLARE @Exist bit

set @Flavor='Vanilla'
set @Exist=1

MERGE IceCream AS T
USING IceCream AS S
ON S.Flavor=T.Flavor AND
    S.Flavor=@Flavor AND
    T.Flavor=@Flavor
WHEN NOT MATCHED BY TARGET AND @Exist=1
    THEN INSERT (Flavor) VALUES (@Flavor)
WHEN NOT MATCHED BY SOURCE AND @Exist=0
    THEN DELETE
OUTPUT $action, inserted.*, deleted.*;
  

Комментарии:

1. Возможно, вы захотите вообще пересмотреть использование СЛИЯНИЯ: mssqltips.com/sqlservertip/3074 /…

Ответ №1:

Хотя существуют разные способы достижения вашей цели, это MERGE утверждение будет работать:

 DECLARE @Flavor varchar(50), @Exist bit;

SET @Flavor = 'Orange';
SET @Exist  = 1;

;WITH TGT AS (SELECT Flavor FROM IceCream WHERE Flavor = @Flavor)
MERGE INTO TGT
USING (VALUES(@Flavor, @Exist)) AS SRC(Flavor, Exist)
   ON TGT.Flavor = SRC.Flavor
WHEN MATCHED AND SRC.Exist = 0
     THEN DELETE
WHEN NOT MATCHED BY TARGET AND SRC.Exist = 1
     THEN INSERT (Flavor) VALUES (SRC.Flavor);
  

Начните с начального списка:

 INSERT INTO IceCream VALUES ('Vanilla'), ('Chocolate'), ('Strawberry')  
  

И тестирование с несколькими разными парами (вкус, существует):

 [Orange,1]
----------
Chocolate, Orange, Strawberry, Vanilla

[Rasberry,0]
----------
Chocolate, Orange, Strawberry, Vanilla

[Vanilla,0]
----------
Chocolate, Orange, Strawberry

[Chocolate,1]
----------
Chocolate, Orange, Strawberry    

[Java,1]
----------
Chocolate, Java, Orange, Strawberry
  

Упрощение:

На самом деле, я использовал CTE в качестве цели, потому что в вашем исходном примере в WHEN NOT MATCHED BY SOURCE THEN DELETE девяти случаях из десяти было и, при синхронизации набора, ваша цель должна быть ограничена подзапросом (CTE в случае СЛИЯНИЯ).

Однако ваши фактические потребности, похоже, не требуют WHEN NOT MATCHED BY SOURCE предложения, и поэтому вы можете упростить приведенное выше утверждение с теми же результатами:

 ;MERGE INTO IceCream AS TGT
USING (VALUES(@Flavor, @Exist)) AS SRC(Flavor, Exist)
   ON TGT.Flavor = SRC.Flavor
WHEN MATCHED AND SRC.Exist = 0
     THEN DELETE
WHEN NOT MATCHED BY TARGET AND SRC.Exist = 1
     THEN INSERT (Flavor) VALUES (SRC.Flavor);
  

Комментарии:

1. Ваш упрощенный ответ очень интуитивно понятен и работает как шарм; спасибо!