sql-server 合并-仅在值发生更改时更新

twh00eeo  于 2022-10-31  发布在  其他
关注(0)|答案(4)|浏览(163)

我正在SQL Server中执行合并。在我的更新中,我只想在值变更时更新数据列。每次更新时,都会有一个版本数据列递增。下面是范例:

MERGE Employee as tgt USING 
(SELECT Employee_History.Emp_ID
, Employee_History.First_Name
, Employee_History.Last_Name
FROM Employee_History)
as src (Emp_ID,First_Name,Last_Name)
ON tgt.Emp_ID = src.Emp_ID
WHEN MATCHED THEN 
    UPDATE SET
    Emp_ID = src.Emp_ID,
    ,[VERSION] = tgt.VERSION + 1 
    ,First_Name = src.First_Name
    ,Last_Name = src.Last_Name
WHEN NOT MATCHED BY target THEN 
    INSERT (Emp_ID,0,First_Name,Last_Name)
VALUES 
    (src.Emp_ID,[VERSION],src.First_Name,src.Last_Name);

现在,如果我只想更新行,并因此增加版本,只有当名称已更改。

qni6mghb

qni6mghb1#

WHEN MATCHED可以有AND。而且,不需要更新EMP_ID

...
 WHEN MATCHED AND (trg.First_Name <> src.First_Name 
   OR trg.Last_Name <> src.Last_Name) THEN UPDATE
   SET 
   [VERSION] = tgt.VERSION + 1 
    ,First_Name = src.First_Name
    ,Last_Name = src.Last_Name
 ...

如果Last_Name或First_Name可以为空,则在比较trg.Last_Name〈〉src.Last_Name时需要注意NULL值,例如ISNULL(trg.Last_Name,'') <> ISNULL(src.Last_Name,'')

mcvgt66p

mcvgt66p2#

a1ex07提供的答案是正确的,但我只是想详细说明比较大量列、查看空值等方面的困难。
我发现我可以使用哈希字节在某些CTE中生成校验和,将这些CTE作为合并的目标,然后使用上面指定的“update and....”条件来比较哈希:

with SourcePermissions as (
    SELECT 1 as Code, 1013 as ObjectTypeCode, 'Create Market' as ActionName, null as ModuleCode, 1 as AssignableTargetFlags
    union all SELECT 2, 1013, 'View Market', null, 1
    union all SELECT 3, 1013, 'Edit Market', null, 1
    --...shortened....
)
,SourcePermissions2 as (
    select sp.*, HASHBYTES('sha2_256', xmlcol)  as [Checksum] 
    from SourcePermissions sp
    cross apply (select sp.* for xml raw) x(xmlcol)
)
,TargetPermissions as (
    select p.*, HASHBYTES('sha2_256', xmlcol)  as [Checksum] 
    from Permission p
    cross apply (select p.* for xml raw) x(xmlcol)
) --select * from SourcePermissions2 sp join TargetPermissions tp on sp.code=tp.code where sp.Checksum = tp.Checksum

    MERGE TargetPermissions AS target  
    USING (select * from SourcePermissions2) AS source ([Code] , [ObjectTypeCode] , [ActionName] , [ModuleCode] , [AssignableTargetFlags], [Checksum])  
        ON (target.Code = source.Code)  
    WHEN MATCHED and source.[Checksum] != target.[Checksum] then
        UPDATE SET [ObjectTypeCode] = source.[ObjectTypeCode], [ActionName]=source.[ActionName], [ModuleCode]=source.[ModuleCode], [AssignableTargetFlags] = source.[AssignableTargetFlags]
    WHEN NOT MATCHED THEN  
        INSERT ([Code] , [ObjectTypeCode] , [ActionName] , [ModuleCode] , [AssignableTargetFlags])  
        VALUES (source.[Code] , source.[ObjectTypeCode] , source.[ActionName] , source.[ModuleCode] , source.[AssignableTargetFlags])
    OUTPUT deleted.*, $action, inserted.[Code] 
        --only minor issue is that you can no longer do a inserted.* here since it gives error 404 (sql, not web), complaining about returning checksum which is included in the target cte but not the underlying table
        ,inserted.[ObjectTypeCode] , inserted.[ActionName] , inserted.[ModuleCode] , inserted.[AssignableTargetFlags]
    ;

注意事项:我本可以用校验和或binary_checksum来大大简化,但我总是会遇到冲突。
至于“为什么”,这是自动部署的一部分,以保持查找表最新。但合并的问题是,有一个复杂的索引视图,大量使用,所以更新相关表是相当昂贵的。

cdmah0mi

cdmah0mi3#

您可以更改[VERSION] + 1代码,在名称匹配时添加零,而不是完全避免更新:

[VERSION] = tgt.VERSION + (CASE
    WHEN tgt.First_Name <> src.First_Name OR tgt.Last_Name <> src.Last_Name
    THEN 1
    ELSE 0 END)
b91juud3

b91juud34#

@a1ex07感谢您的回答..稍有更正..我没有遵循SQL版本,因此这可能是SQL规范中的更改
当匹配和条件时,则更新
上述语法无效
以下内容有效
如果匹配,则更新集合...WHERE条件如果不匹配,则插入...
因此将其更改为

WHEN MATCHED THEN UPDATE
   SET 
   [VERSION] = tgt.VERSION + 1 
   ,First_Name = src.First_Name
   ,Last_Name = src.Last_Name

WHERE目标名〈〉源名OR目标姓〈〉源姓

https://docs.oracle.com/cd/B28359_01/server.111/b28286/statements_9016.htm#SQLRF01606

相关问题