我和谢尔盖在一起!如果某个数量(或所有)源和目标记录匹配,那么我可能还想避免进行任何不必要的更新!
我明白为什么人们可能认为这无关紧要.. 在某些或很多情况下,用完全相同的数据替换一定数量的数据并不重要,除非您使用触发器跟踪审计类型信息或使用触发器进行事务日志记录,那么它可能很重要!
Martin Smith 的 WHEN MATCHED AND EXISTS(SELECT ... EXCEPT SELECT .. ) 是一个很好的评论,我尝试使用它。我想知道这是否只是在所有记录都匹配但不匹配时才避免进行更新......它就像我希望的那样......(我手头有一个极端的情况,有几 100 列和约 250,000 行)。请参阅下面的较小代码示例!
另一种选择可能是使用 SSIS 和渐变维度组件。
一篇好文章在这里:
http://www.made2mentor.com/2013/05/writing-t-sql-merge-statements-the-right-way/
一个完整的代码示例,根据 Martin Smith 的评论使用代码以避免进行不必要的记录更新,并处理空值,是这个示例:
CREATE TABLE Clinical.AAATargetTable (
category_id INT PRIMARY KEY,
category_name VARCHAR(255) NOT NULL,
amount DECIMAL(10 , 2 )
);
INSERT INTO Clinical.AAATargetTable(category_id, category_name, amount)
VALUES(1,'Children Bicycles',15000),
(2,'Comfort Bicycles',25000),
(3,'Cruisers Bicycles',13000),
(4,'Cyclocross Bicycles',10000);
CREATE TABLE Clinical.AAASourceTable (
category_id INT PRIMARY KEY,
category_name VARCHAR(255) NOT NULL,
amount DECIMAL(10 , 2 )
);
INSERT INTO Clinical.AAASourceTable(category_id, category_name, amount)
VALUES(1,'Children Bicycles',15000),
(3,'Cruisers Bicycles',13000),
(4,'Cyclocross Bicycles',20000),
(5,'Electric Bikes',10000),
(6,'Mountain Bikes',10000);
MERGE Clinical.AAATargetTable t
USING Clinical.AAASourceTable s
ON (s.category_id = t.category_id)
WHEN MATCHED AND EXISTS (SELECT s.category_id,s.category_name,s.amount EXCEPT SELECT t.category_id,t.category_name,t.amount )
THEN UPDATE SET
t.category_name = s.category_name,
t.amount = s.amount
WHEN NOT MATCHED BY TARGET
THEN INSERT (category_id, category_name, amount)
VALUES (s.category_id, s.category_name, s.amount)
WHEN NOT MATCHED BY SOURCE
THEN DELETE;
UPDATE Clinical.AAASourceTable set amount =1234 where category_id=1;
MERGE Clinical.AAATargetTable t
USING Clinical.AAASourceTable s
ON (s.category_id = t.category_id)
WHEN MATCHED AND EXISTS (SELECT s.category_id,s.category_name,s.amount EXCEPT SELECT t.category_id,t.category_name,t.amount )
THEN UPDATE SET
t.category_name = s.category_name,
t.amount = s.amount
WHEN NOT MATCHED BY TARGET
THEN INSERT (category_id, category_name, amount)
VALUES (s.category_id, s.category_name, s.amount)
WHEN NOT MATCHED BY SOURCE
THEN DELETE;
DROP TABLE Clinical.AAATargetTable;
DROP TABLE Clinical.AAASourceTable;
另一个评论是,如果 MERGE 仅执行 UPDATE,则 MERGE 可以重写为 UPDATE 语句。UPDATE 语句将比 MERGE 执行得更好。根据上述示例检测数据更改的技术也可以应用于 UPDATE 语句:即。.. 在 WHERE 子句中 ... AND EXISTS (SELECT ... EXCEPT SELECT ...)
作为 UPDATE 语句的一部分,一个更大的非平凡示例是 as per this(抱歉这里没有给出一个简单的例子,如果时间允许,会回到这个)。
UPDATE dbo.ContractCampusVersions
SET ICULevel = s.ICULevel,
SecondTierCategory = s.SecondTierCategory
FROM
(
SELECT
I.ProviderNumber,
I.AgreementNumber,
'Main' AS CampusID,
CCV.Version,
PC.AHSACampusID AS ICULevel,
PCR.Tier2Cat AS SecondTierCategory
FROM
dbo.ImportAHSAHospitalDetails I
JOIN dbo.ProviderCampuses PC ON (I.ProviderNumber = PC.ProviderID) and (PC.CampusID = 'Main')
JOIN dbo.ProviderCampusRevisions PCR ON (PCR.ProviderID=I.ProviderNumber) AND (PCR.CampusID = 'Main')
AND PCR.EffDate =
(
SELECT MAX(EffDate)
FROM dbo.ProviderCampusRevisions
WHERE ProviderID = I.ProviderNumber
AND CampusID = 'Main'
)
JOIN dbo.ContractCampusVersions CCV on (I.AgreementNumber = CCV.AgreementID) and (I.ProviderNumber = CCV.ProviderID) and (CCV.CampusID = 'Main')
and CCV.Version = (select max(Version)
from dbo.ContractCampusVersions CCVM where CCVM.AgreementID = CCV.AgreementID and CCVM.ProviderID = CCV.ProviderID and CCVM.CampusID = 'Main'
)
WHERE
I.AgreementNumber IS NOT NULL
AND I.ClosedDate IS NULL
) s
WHERE (s.AgreementNumber = ContractCampusVersions.AgreementID) AND (s.ProviderNumber = ContractCampusVersions.ProviderID) AND (ContractCampusVersions.CampusID = 'Main') AND (ContractCampusVersions.Version = s.Version)
AND EXISTS
(
SELECT
s.ICULevel,
s.SecondTierCategory
EXCEPT
SELECT
ContractCampusVersions.ICULevel,
ContractCampusVersions.SecondTierCategory
)
;