将多个更新与条件相结合,更好地合并?

lb3vh1jj  于 2022-10-03  发布在  其他
关注(0)|答案(3)|浏览(423)

SQL Server Merge: update only changed data, tracking changes?的后续问题

我们一直在努力让有效的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

...每一栏。

第二项质询。

我们是否可以将已更新的记录记录到单独的表/变量中?

合并将是完美的,但是我们无法看到更新了哪条记录,因为从输出返回的数据显示了所有行,因为目标总是更新的。

编辑完全合并:

M

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'
shstlldc

shstlldc1#

可以将单个UPDATEOUTPUT子句一起使用,并在JOIN子句中使用INTERSECTEXCEPT子查询来检查是否有任何列已更改。

例如

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)
;
8ehkhllq

8ehkhllq2#

在处理数据仓库维度中的值时,我们也有类似的需求。合并可以很好地工作,但对于大型表可能效率较低。您的方法可以工作,但效率似乎相当低,因为您将对每个列都有单独的更新。缩短内容的一种方法是比较一条语句中的多个列(这显然会使事情变得更加复杂)。您似乎也没有考虑空值。

我们最终使用的基本上是本页面中描述的技术:https://sqlsunday.com/2016/07/14/comparing-nullable-columns/

使用INTERSECT,您可以轻松(快速)比较我们的临时表和维度表之间的差异,而不必为每个单独的列显式编写比较。

为了回答您的第二个问题,上面的技术不能让您捕捉到哪一列发生了更改。但是,您可以比较旧行和新行(我们通过设置“ValidTo”日期来“关闭”该行的早期版本,然后添加“ValidFrom”日期等于今天日期的新行。

我们的代码最终如下所示:

  • INSERT Stage表中在新表中没有匹配键值的所有行(新行)
  • 使用INTERSECT比较阶段和维度,并将所有匹配项存储在表变量中
  • 使用表变量“关闭”维度中所有匹配的行
  • 使用表变量INSERT新行
  • 如果发生满载,我们还可以检查只存在于维度中但不存在于Stage表中的键。这将指示这些行已在源系统中删除,并且我们在维度中将它们标记为“IsDelete”。
col17t5w

col17t5w3#

我认为你可能过多地考虑了复杂性,但是的。您的底层更新是基于每个查询中匹配的ID在ADS组和临时表之间进行比较。由于您已经在检查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

因此,您想要的最终值是将描述和显示从Stage拉回到ADS Groups表中。

在上面的示例中,我有三个示例,如果基于匹配的ID提供条目,则需要更改。如果一列中的值相同,但另一列中的值不同,并且您更新了这两列,则最终的结果是更新了一列错误的列。第一个最终将保持不变。如果两者都不同,无论如何都会更新。

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

然后,你总是有正确的描述和显示名称,而不必在它们之间保持同步更新。

相关问题