2

有没有办法避免更新合并语句中“新值”不变的列?
我仍然需要更新,因为我需要从输出子句中的插入表中检索字段。

目标当然是避免 IO 写入

WHEN MATCHED THEN
    UPDATE
    SET 
        productName = Src.productName -- Only if Src.productName <> productName
        , productNameModel = Src.productNameModel -- Only if Src.productNameModel <> productNameModel
        , brandID = Src.brandID -- Only if Src.brandID <> brandID
        , ean = Src.ean -- Only if Src.ean <> ean
        , categoryID = Src.categoryID -- Only if Src.categoryID <> categoryID
        , resellerPrijsEx = Src.resellerPrijsEx -- Only if Src.resellerPrijsEx <> resellerPrijsEx
        , inStock = Src.inStock -- Only if Src.inStock <> inStock
        , warrantyID = Src.warrantyID -- Only if Src.warrantyID <> warrantyID
        , productNamePn = Src.productNamePn -- Only if Src.productNamePn <> productNamePn
OUTPUT Src.ID, inserted.ID, Src.hasDescription INTO @tblID (tmpID, ID, hasDescription) -- Yet UPDATE is required since I need to retrieve inserted ID
;
4

2 回答 2

3

如果您跳过一些字段而不更新它们,我认为这并不重要。SQL Server 不会为更新每个字段进行单独的 I/O 操作,并且其 I/O 操作经过高度优化,因此跳过几个字段不会有任何影响(如果一条记录适合一页,至少)。

查看更多关于 SQL Server 如何将页面写入磁盘的信息,例如:Writing Pages。您会发现在实践中跳过字段不会对性能产生影响,但会使您的代码复杂化。

于 2013-10-16T11:48:06.700 回答
3

我和谢尔盖在一起!如果某个数量(或所有)源和目标记录匹配,那么我可能还想避免进行任何不必要的更新!

我明白为什么人们可能认为这无关紧要.. 在某些或很多情况下,用完全相同的数据替换一定数量的数据并不重要,除非您使用触发器跟踪审计类型信息或使用触发器进行事务日志记录,那么它可能很重要!

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                  
    )
;
于 2019-03-29T03:26:01.733 回答