我在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);
现在,如果我只想更新行,从而增加版本,只要名称已更改。
答案 0 :(得分:34)
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&lt;&gt;时需要处理NULL
值。 src.Last_Name,例如ISNULL(trg.Last_Name,'') <> ISNULL(src.Last_Name,'')
答案 1 :(得分:1)
您可以更改[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)
答案 2 :(得分:1)
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]
;
注意事项:本来可以用checksum或binary_checksum大大简化,但是我总是与它们发生冲突。
关于“为什么”,这是自动部署的一部分,可以使查找表保持最新状态。但是,合并的问题在于存在一个索引视图,该索引非常复杂且使用率很高,因此对相关表的更新非常昂贵。
答案 3 :(得分:-1)
@ a1ex07感谢您的回答..稍作更正..我没有遵循SQL版本,因此这可能是SQL规范的变化
匹配条件然后更新
以上是无效的语法
以下内容有效
何时匹配然后更新集... 条件何时不匹配然后插入...
因此将其更改为
WHEN MATCHED THEN UPDATE
SET
[VERSION] = tgt.VERSION + 1
,First_Name = src.First_Name
,Last_Name = src.Last_Name
位置 trg.First_Name <> src.First_Name 或trg.Last_Name <> src.Last_Name
https://docs.oracle.com/cd/B28359_01/server.111/b28286/statements_9016.htm#SQLRF01606