[英]Combine multiple updates with conditions, better merge?
我们一直在努力使有效的合并语句起作用,现在正在考虑仅使用更新,我们有一个非常简单的问题:从值不同的源更新目标并记录更改,两个表的布局相同。
所以,我们有两个问题:是否可以将这个非常简单的更新合并到一个语句中?
UPDATE tbladsgroups
SET tbladsgroups.Description = s.Description,
tbladsgroups.action='Updated'
FROM tbladsgroups t
INNER JOIN tbladsgroups_staging s
ON t.SID = s.SID
Where s.Description <> t.Description
UPDATE tbladsgroups
SET tbladsgroups.DisplayName = s.DisplayName,
tbladsgroups.action='Updated'
FROM tbladsgroups t
INNER JOIN tbladsgroups_staging s
ON t.SID = s.SID
Where s.DisplayName <> t.DisplayName
....对于每一列。
第二个问题。
我们可以将更新的记录记录到单独的表/变量中吗?
合并将是完美的,但是我们无法看到更新了哪条记录,因为从 OUTPUT 返回的数据显示了所有行,因为目标始终在更新。
编辑完整合并:
米
ERGE tblADSGroups AS TARGET
USING tblADSGroups_STAGING AS SOURCE
ON (TARGET.[SID] = SOURCE.[SID])
WHEN MATCHED
THEN UPDATE SET
TARGET.[Description]=CASE
WHEN source.[Description] != target.[Description] THEN(source.[Description]
)
ELSE target.[Description] END,
TARGET.[displayname] = CASE
WHEN source.[displayname] != target.[displayname] THEN source.[displayname]
ELSE target.[displayname] END
...other columns cut for brevity
WHEN NOT MATCHED BY TARGET
THEN
INSERT (
[SID],[SamAccountName],[DisplayName],[Description],[DistinguishedName],[GroupCategory],[GroupScope],[Created],[Members],[MemberOf],[SYNCtimestamp],[Action]
)
VALUES (
source.[SID],[SamAccountName],[DisplayName],[Description],[DistinguishedName],[GroupCategory],[GroupScope],[Created],[Members],[MemberOf],[SYNCtimestamp],[Action]
)
WHEN NOT MATCHED BY SOURCE
THEN
UPDATE SET ACTION='Deleted'
在处理数据仓库维度中的值时,我们有类似的需求。 合并工作正常,但对于大表可能效率低下。 您的方法可行,但似乎效率很低,因为您会对每一列进行单独更新。 缩短事情的一种方法是比较一个语句中的多个列(这显然会使事情变得更复杂)。 您似乎也没有考虑 NULL 值。
我们最终使用的基本上是此页面上描述的技术: https://sqlsunday.com/2016/07/14/comparing-nullable-columns/
使用INTERSECT
可以让您轻松(快速)比较暂存表和维度表之间的差异,而无需为每个单独的列显式编写比较。
要回答你的第二个问题,上面的技术不会让你捕捉到哪一列发生了变化。 但是,您可以比较旧行与新行(我们通过设置“ValidTo”日期来“关闭”该行的早期版本,然后添加“ValidFrom”日期等于今天的新行。
我们的代码最终看起来像下面这样:
INSERT
阶段表中在新表中没有匹配键值的所有行(新行)INTERSECT
比较阶段与维度并将所有匹配项存储在表变量中INSERT
新行 您可以使用带有OUTPUT
子句的单个UPDATE
,并在连接子句中使用INTERSECT
或EXCEPT
子查询来检查是否有任何列已更改。
例如
UPDATE t
SET Description = s.Description,
DisplayName = s.DisplayName,
action = 'Updated'
OUTPUT inserted.ID, inserted.Description, inserted.DisplayName
INTO @tbl (ID, Description, DisplayName)
FROM tbladsgroups t
INNER JOIN tbladsgroups_staging s
ON t.SID = s.SID
AND NOT EXISTS (
SELECT s.Description, s.DisplayName
INTERSECT
SELECT t.Description, t.DisplayName
);
如果你还想INSERT
,你可以用MERGE
做类似的事情
MERGE tbladsgroups t
USING tbladsgroups_staging s
ON t.SID = s.SID
WHEN MATCHED AND NOT EXISTS ( -- do NOT place this condition in the ON
SELECT s.Description, s.DisplayName
INTERSECT
SELECT t.Description, t.DisplayName
)
THEN UPDATE SET
Description = s.Description,
DisplayName = s.DisplayName,
action = 'Updated'
WHEN NOT MATCHED
THEN INSERT (ID, Description, DisplayName)
VALUES (s.ID, s.Description, s.DisplayName)
OUTPUT inserted.ID, inserted.Description, inserted.DisplayName
INTO @tbl (ID, Description, DisplayName)
;
我认为您可能过度考虑了复杂性,但是是的。 您的基础更新是根据每个查询中的匹配 ID 对广告组和登台表进行比较。 由于您已经在检查 ID 上的连接并比较不同的描述或显示名称,因此只需更新这两个字段。 为什么?
groups description groups display staging description staging display
SomeValue Show Me SOME other Value Show Me
Try This Attempt Try This Working on it
Both Diff Changes Both Are Diff Change Me
因此,您想要的最终价值是将描述和展示从登台拉回广告组表。
在上面的示例中,我有三个示例,如果基于匹配的 ID 显示需要更改的条目。 如果一列中的值相同,而另一列中的值不同,并且您更新了两列,则 .net 效果是更新的一个坏列。 第一个最终将保持不变。 如果两者不同,无论如何都会更新。
UPDATE tbladsgroups
SET tbladsgroups.Description = s.Description,
tbladsgroups.DisplayName = s.DisplayName,
tbladsgroups.action='Updated'
FROM tbladsgroups t
INNER JOIN tbladsgroups_staging s
ON t.SID = s.SID
Where s.Description <> t.Description
OR s.DisplayName <> t.DisplayName
现在,所有这些解决方案都在说,你有冗余数据,这就是查找表的全部意义所在。 暂存似乎始终具有正确的显示名称和描述。 您的 tblAdsGroups 可能应该删除这两列,并始终将它们从登台开始...
select
t.*,
s.Description,
s.DisplayName
from
tblAdsGroups t
JOIN tblAdsGroups_Staging s
on t.sid = s.sid
然后您始终拥有正确的描述和显示名称,而不必在它们之间保持同步更新。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.