我可以在 MERGE 语句中对目标应用 WHERE 子句吗?

Posted

技术标签:

【中文标题】我可以在 MERGE 语句中对目标应用 WHERE 子句吗?【英文标题】:Can I apply a WHERE clause on the target in a MERGE statement? 【发布时间】:2012-06-28 10:00:46 【问题描述】:

我有一个目标表,其中包含一个带有IsActive 标志的项目,并且我正在使用MERGE 语句从源表中插入和更新。如果源表中存在某些内容,则它处于活动状态,如果不存在,则它不处于活动状态。逻辑很简单:

如果它存在于源和目标中,该行应该有IsActivetrue 如果它仅存在于源中,则应将新行插入到目标中,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 将不匹配,因此无论是否包含该附加条件,它将被设置为非活动状态。

最终结果是,每当为源系统运行该过程时,所有其他系统都将设置为非活动状态。

到目前为止,我的解决方案是只为MATCHEDNOT MATCHED BY TARGET 运行MERGE,然后为不匹配的行运行后续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 SourceID=@sourceID 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 SourceID=@SourceID 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

【讨论】:

如果您尝试这样做,您会看到它为有问题的行提供3 2 0。原因是当您加入 t.id = source.id 时,第 3 行没有 t。它与第 2 行相同并标记为活动。 @KirkBroadhurst 它不适合我!查看上面的完整测试 啊,我明白了,我读得太快了。很棒的答案谢谢!我不知道我可以在那里放一个条件。

以上是关于我可以在 MERGE 语句中对目标应用 WHERE 子句吗?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 HIVE 在 WHERE 语句中对 OR 子句进行分组

Sql 函数,我在另一个“where 语句”中对一组数据使用“where 语句”

arcgis中对要素的merge和union操作有何不同

merge into using 详解

MS SQL 技巧总结--持续更新

oracle merge into用法