Могу ли я применить предложение WHERE к цели в заявлении MERGE?

У меня есть целевая таблица, содержащая элементы с флагом IsActive, и я вставляю и обновляю исходную таблицу с помощью инструкции MERGE. Если что-то существует в исходной таблице, то оно активно, а если нет, то оно не активно. Логика довольно проста:

  • если он существует в источнике и цели, строка должна иметь IsActive true
  • если он существует только в источнике, тогда в цель должна быть добавлена ​​новая строка с IsActive true
  • если он существует только в целевой, тогда IsActive должен быть установлен в false.

Все очень просто, за исключением того, что целевая таблица также имеет различающийся столбец SourceId, который относится к исходной таблице. Поэтому для данной исходной таблицы я хочу только MERGE от строк с соответствующим SourceId.

(Моя нормализованная таблица содержит строки одинаковых типов данных из нескольких систем - я извлекаю данные из этих систем отдельно и, следовательно, необходимо объединить из одного источника за раз)

Вот пример:

IF OBJECT_ID('tempdb..#target') IS NOT NULL DROP TABLE #target    
IF OBJECT_ID('tempdb..#source') IS NOT NULL DROP TABLE #source

CREATE TABLE #target  ( Id INT, SourceId INT, IsActive BIT )   
INSERT #target VALUES (1, 1, 0)
INSERT #target VALUES (2, 1, 1)
INSERT #target VALUES (3, 2, 1)

CREATE TABLE #source ( Id INT )    
INSERT #source VALUES (1)
INSERT #source VALUES (4)

DECLARE @SourceId INT = 1;    
SELECT * FROM #target

MERGE INTO #target t
USING
(
    SELECT [Id] FROM #source
) AS s
ON t.[Id] = s.[Id] AND t.[SourceId] = @SourceId
WHEN MATCHED THEN UPDATE SET [IsActive] = 1
WHEN NOT MATCHED BY TARGET THEN INSERT VALUES ([Id], @SourceId, 1)
WHEN NOT MATCHED BY SOURCE THEN UPDATE SET [IsActive] = 0;

SELECT * FROM #target

Моя первоначальная попытка состояла в том, чтобы включить AND t.[SourceId] = @SourceId в условие слияния, но, очевидно, это не сработает - оно ограничивает элементы для объединения, но не целевую таблицу. Целевая строка ID = 3 не будет соответствовать, и поэтому она будет установлена ​​в неактивную, независимо от того, включено ли это дополнительное условие.

Конечным результатом является то, что всякий раз, когда процедура запускается для исходной системы, все остальные системы будут установлены как неактивные.

Мое решение до сих пор заключается в том, чтобы запустить MERGE только для MATCHED и NOT MATCHED BY TARGET, а затем запустить следующий UPDATE для непревзойденных строк

UPDATE #target
SET [IsEnabled] = 0 
WHERE [SourceId] = @SourceId
AND [ID] NOT IN (SELECT [ID] FROM #source)

Можно ли включить это условие фильтра в оператор MERGE? Есть ли какие-нибудь другие умные способы достичь этого?

Ответ 1

Таким образом, ваш результирующий набор должен быть

1 1 1
2 1 0    
3 2 1
4 1 1

в этом случае ваш оператор слияния должен быть

merge #target as t
using #source as source
on (t.id=source.id)
when matched then update set isactive=1
when not matched by target then insert values (id, @sourceid,1)
when not matched by source and [email protected] then update set isactive=0 

Полный тест:

CREATE TABLE #target  ( Id INT, SourceId INT, IsActive BIT )    
INSERT #target VALUES (1, 1, 0) 
INSERT #target VALUES (2, 1, 1) 
INSERT #target VALUES (3, 2, 1) 

CREATE TABLE #source ( Id INT )     
INSERT #source VALUES (1) 
INSERT #source VALUES (4) 

DECLARE @SourceId INT 
select @SourceId = 1;     

merge #target as t 
using #source as source 
on (t.id=source.id) 
when matched then update set isactive=1 
when not matched by target then insert values (id, @sourceid,1) 
when not matched by source and [email protected] then update set isactive=0;


SELECT * FROM #target 

drop table #target;
drop table #source

Результаты...

Id          SourceId    IsActive
----------- ----------- --------
1           1           1
2           1           0
3           2           1
4           1           1