SQL Server Merge,如何按日期范围(年)子集目标

时间:2016-06-20 04:59:18

标签: sql sql-server merge subset

我一直在阅读SQL Server中的MERGE语法,它非常适合我需要它做的事情,但我不能为我的生活找出如何防止目标表删除旧数据我不想与之匹敌。

我在Target中有1100万行,我只希望将当前年份与大约300k行的变化相匹配。显然会有巨大的性能差异。

我的代码:

    MERGE  [dbo].[RE_Gifts_Backup_testing] as Target   --[TARGET is main table]
    USING  [dbo].[RE_Gifts_CY_Changes] as Source  --[SOURCE is data with new changes]

    ON (Target.gift_id = Source.gift_id)  -- What are we matching rows on.




    --When rows are matched, update the records if there is any change

    WHEN MATCHED
            AND year(Target.gift_date) = '2016'
            AND year(Source.Gift_Date) = '2016' 
            AND (TARGET.[Constituent_ID] <> SOURCE.[Constituent_ID]
            OR  TARGET.[RE_Gift_ID] <> SOURCE.[RE_Gift_ID] 
            OR  TARGET.[Gift_ID] <> SOURCE.[Gift_ID] 
            OR  TARGET.[Gift_Date_Added] <> SOURCE.[Gift_Date_Added] 
            OR  TARGET.[Gift_Date] <> SOURCE.[Gift_Date] 
            OR  TARGET.[Name] <> SOURCE.[Name] 
            OR  TARGET.[Gift_Type] <> SOURCE.[Gift_Type] 
            OR  TARGET.[Gift_Amount] <> SOURCE.[Gift_Amount] 
            OR  TARGET.[Frequency] <> SOURCE.[Frequency] 
            OR  TARGET.[Pay_Method] <> SOURCE.[Pay_Method]
            OR  TARGET.[Appeal] <> SOURCE.[Appeal]
            OR  TARGET.[Campaign] <> SOURCE.[Campaign]
            OR  TARGET.[Gift Added By] <> SOURCE.[Gift Added By]
            OR  TARGET.[Gift Reference] <> SOURCE.[Gift Reference]
            OR  TARGET.[SoftCredit] <> SOURCE.[SoftCredit]
            OR  TARGET.[Relationship Manager] <> SOURCE.[Relationship Manager]
            OR  TARGET.[CostCentre] <> SOURCE.[CostCentre] 
            OR  TARGET.[Recruiter] <> SOURCE.[Recruiter] 
            OR  TARGET.[Sitecode] <> SOURCE.[Sitecode] 
            OR  TARGET.[Zerodebit] <> SOURCE.[Zerodebit] 
            OR  TARGET.[Donation_Channel] <> SOURCE.[Donation_Channel] 
            OR  TARGET.[Source_Channel] <> SOURCE.[Source_Channel] 
            OR  TARGET.[Recruitment_Source] <> SOURCE.[Recruitment_Source] 
            OR  TARGET.[Merch_ProductID] <> SOURCE.[Merch_ProductID] )


        THEN UPDATE 
            set 
              TARGET.[Constituent_ID] = SOURCE.[Constituent_ID],
              TARGET.[RE_Gift_ID] = SOURCE.[RE_Gift_ID],
              TARGET.[Gift_ID] = SOURCE.[Gift_ID], 
              TARGET.[Gift_Date_Added] = SOURCE.[Gift_Date_Added], 
              TARGET.[Gift_Date] = SOURCE.[Gift_Date], 
              TARGET.[Name] = SOURCE.[Name],
              TARGET.[Gift_Type] = SOURCE.[Gift_Type],
              TARGET.[Gift_Amount] = SOURCE.[Gift_Amount],
              TARGET.[Frequency] = SOURCE.[Frequency],
              TARGET.[Pay_Method] = SOURCE.[Pay_Method],
              TARGET.[Appeal] = SOURCE.[Appeal],
              TARGET.[Campaign] = SOURCE.[Campaign],
              TARGET.[Gift Added By] = SOURCE.[Gift Added By],
              TARGET.[Gift Reference] = SOURCE.[Gift Reference],
              TARGET.[SoftCredit] = SOURCE.[SoftCredit],
              TARGET.[Relationship Manager] = SOURCE.[Relationship Manager],
              TARGET.[CostCentre] = SOURCE.[CostCentre],
              TARGET.[Recruiter] = SOURCE.[Recruiter],
              TARGET.[Sitecode] = SOURCE.[Sitecode],
              TARGET.[Zerodebit] = SOURCE.[Zerodebit],
              TARGET.[Donation_Channel] = SOURCE.[Donation_Channel],
              TARGET.[Source_Channel] = SOURCE.[Source_Channel],
              TARGET.[Recruitment_Source] = SOURCE.[Recruitment_Source],
              TARGET.[Merch_ProductID] = SOURCE.[Merch_ProductID]

    -- when no records are matched then insert from source into target.
    WHEN NOT MATCHED BY TARGET and year(target.gift_date) >= '2016' THEN 

    INSERT ([Constituent_ID],[RE_Gift_ID],[Gift_ID],[Gift_Date_Added],[Gift_Date],[Name],[Gift_Type],[Gift_Amount],[Frequency],[Pay_Method],[Appeal],[Campaign],[Gift Added By],[Gift Reference],
                [SoftCredit],[Relationship Manager],[CostCentre],[Recruiter],[Sitecode],[Zerodebit],[Donation_Channel],[Source_Channel],[Recruitment_Source],[Merch_ProductID])

    VALUES (source.[Constituent_ID],source.[RE_Gift_ID],source.[Gift_ID],source.[Gift_Date_Added],source.[Gift_Date],source.[Name],source.[Gift_Type],source.[Gift_Amount],source.[Frequency],
                source.[Pay_Method],source.[Appeal],source.[Campaign],source.[Gift Added By],source.[Gift Reference],source.[SoftCredit],source.[Relationship Manager],source.[CostCentre],
                source.[Recruiter],source.[Sitecode],source.[Zerodebit],source.[Donation_Channel],source.[Source_Channel],source.[Recruitment_Source],source.[Merch_ProductID])

    --When there is a row that exists in target table and same record does not exist in source table then delete this record from target table
    WHEN NOT MATCHED BY SOURCE and year(target.gift_date) >= '2016' THEN 
    DELETE

    OUTPUT $action, 
            deleted.[Constituent_ID] as [deletedConstituent_ID],
            deleted.[RE_Gift_ID] as [deletedRE_Gift_ID],
            deleted.[Gift_ID] as [deletedGift_ID], 
            deleted.[Gift_Date_Added] as [deletedGift_Date_Added], 
            deleted.[Gift_Date] as [deletedGift_Date], 
            deleted.[Name] as [deletedName],
            deleted.[Gift_Type] as [deletedGift_Type],
            deleted.[Gift_Amount] as [deletedGift_Amount],
            deleted.[Frequency] as [deletedFrequency],
            deleted.[Pay_Method] as [deletedPay_Method],
            deleted.[Appeal] as [deletedAppeal],
            deleted.[Campaign] as [deletedCampaign],
            deleted.[Gift Added By] as [deletedGift Added By],
            deleted.[Gift Reference] as [deletedGift Reference],
            deleted.[SoftCredit] as [deletedSoftCredit],
            deleted.[Relationship Manager] as [deletedRelationship Manager],
            deleted.[CostCentre] as [deletedCostCentre],
            deleted.[Recruiter] as [deletedRecruiter],
            deleted.[Sitecode] as [deletedSitecode],
            deleted.[Zerodebit] as [deletedZerodebit],
            deleted.[Donation_Channel] as [deletedDonation_Channel],
            deleted.[Source_Channel] as [deletedSource_Channel],
            deleted.[Recruitment_Source] as [deletedRecruitment_Source],
            deleted.[Merch_ProductID] as [deletedMerch_ProductID],

            inserted.[Constituent_ID] as [insertedConstituent_ID],
            inserted.[RE_Gift_ID] as [insertedRE_Gift_ID],
            inserted.[Gift_ID] as [insertedGift_ID], 
            inserted.[Gift_Date_Added] as [insertedGift_Date_Added], 
            inserted.[Gift_Date] as [insertedGift_Date], 
            inserted.[Name] as [insertedName],
            inserted.[Gift_Type] as [insertedGift_Type],
            inserted.[Gift_Amount] as [insertedGift_Amount],
            inserted.[Frequency] as [insertedFrequency],
            inserted.[Pay_Method] as [insertedPay_Method],
            inserted.[Appeal] as [insertedAppeal],
            inserted.[Campaign] as [insertedCampaign],
            inserted.[Gift Added By] as [insertedGift Added By],
            inserted.[Gift Reference] as [insertedGift Reference],
            inserted.[SoftCredit] as [insertedSoftCredit],
            inserted.[Relationship Manager] as [insertedRelationship Manager],
            inserted.[CostCentre] as [insertedCostCentre],
            inserted.[Recruiter] as [insertedRecruiter],
            inserted.[Sitecode] as [insertedSitecode],
            inserted.[Zerodebit] as [insertedZerodebit],
            inserted.[Donation_Channel] as [insertedDonation_Channel],
            inserted.[Source_Channel] as [insertedSource_Channel],
            inserted.[Recruitment_Source] as [insertedRecruitment_Source],
            inserted.[Merch_ProductID] as [insertedMerch_ProductID],

            updated.[Constituent_ID] as [updatedConstituent_ID],
            updated.[RE_Gift_ID] as [updatedRE_Gift_ID],
            updated.[Gift_ID] as [updatedGift_ID], 
            updated.[Gift_Date_Added] as [updatedGift_Date_Added], 
            updated.[Gift_Date] as [updatedGift_Date], 
            updated.[Name] as [updatedName],
            updated.[Gift_Type] as [updatedGift_Type],
            updated.[Gift_Amount] as [updatedGift_Amount],
            updated.[Frequency] as [updatedFrequency],
            updated.[Pay_Method] as [updatedPay_Method],
            updated.[Appeal] as [updatedAppeal],
            updated.[Campaign] as [updatedCampaign],
            updated.[Gift Added By] as [updatedGift Added By],
            updated.[Gift Reference] as [updatedGift Reference],
            updated.[SoftCredit] as [updatedSoftCredit],
            updated.[Relationship Manager] as [updatedRelationship Manager],
            updated.[CostCentre] as [updatedCostCentre],
            updated.[Recruiter] as [updatedRecruiter],
            updated.[Sitecode] as [updatedSitecode],
            updated.[Zerodebit] as [updatedZerodebit],
            updated.[Donation_Channel] as [updatedDonation_Channel],
            updated.[Source_Channel] as [updatedSource_Channel],
            updated.[Recruitment_Source] as [updatedRecruitment_Source],
            updated.[Merch_ProductID] as [updatedMerch_ProductID];



    SELECT @@ROWCOUNT;
    GO

这会引发错误:

  

Msg 5334,Level 16,State 2,Line 86
  标识符&#39; target.gift_date&#39;无法约束。在“不匹配源”的情况下,只允许子句范围内的目标列和列。 MERGE声明的条款。

1 个答案:

答案 0 :(得分:4)

这是一个完整的脚本,演示了我是如何做到的。签入SQL Server 2008。

示例数据

DECLARE @TDst TABLE (ID int, dt date, DataValue int);
DECLARE @TSrc TABLE (ID int, dt date, DataValue int);

INSERT INTO @TDst (ID, dt, DataValue) VALUES
 (11, '2015-01-01', 1100) -- don't delete
,(12, '2015-02-02', 1200) -- don't delete
,(21, '2016-01-01', 2100) -- this should be deleted
,(22, '2016-02-02', 2200) -- this would remain as is
,(23, '2016-03-03', 2300) -- this would be updated
;

INSERT INTO @TSrc (ID, dt, DataValue) VALUES
 (22, '2016-02-02', 2200) -- same date and value, don't update
,(23, '2016-03-03', 2388) -- update
,(24, '2016-04-04', 2488) -- add
;

SELECT * FROM @TDst;

+----+------------+-----------+
| ID |     dt     | DataValue |
+----+------------+-----------+
| 11 | 2015-01-01 |      1100 |
| 12 | 2015-02-02 |      1200 |
| 21 | 2016-01-01 |      2100 |
| 22 | 2016-02-02 |      2200 |
| 23 | 2016-03-03 |      2300 |
+----+------------+-----------+

<强>查询

MERGE INTO @TDst AS Dst
USING @TSrc as Src
ON (Dst.ID = Src.ID)

--When rows are matched, update the records if there is any change
WHEN MATCHED
    AND (Dst.dt <> Src.dt 
    OR Dst.DataValue <> Src.DataValue)
THEN 
UPDATE 
SET
     Dst.dt = Src.dt
    ,Dst.DataValue = Src.DataValue

-- when no records are matched then insert from source into target.
WHEN NOT MATCHED BY TARGET THEN
INSERT (ID, dt, DataValue)
VALUES (Src.ID, Src.dt, Src.DataValue)

-- When there is a row that exists in target table and 
-- same record does not exist in source table then delete this record from target table
WHEN NOT MATCHED BY SOURCE AND Dst.dt >= '2016-01-01' THEN 
DELETE
;

SELECT * FROM @TDst;

<强>结果

+----+------------+-----------+
| ID |     dt     | DataValue |
+----+------------+-----------+
| 11 | 2015-01-01 |      1100 |
| 12 | 2015-02-02 |      1200 |
| 22 | 2016-02-02 |      2200 |
| 23 | 2016-03-03 |      2388 |
| 24 | 2016-04-04 |      2488 |
+----+------------+-----------+

正如您所看到的那样,2016年之前的数据得以保留,但2016年的数据已被替换。

如果您的Source表仅包含2016年的数据,那么您只需要在>= '2016-01-01'子句中添加额外的过滤器WHEN NOT MATCHED BY SOURCE,以防止在2016年之前删除行。

您获得的错误很可能是由以下额外过滤器引起的,这是不需要的:

WHEN NOT MATCHED BY TARGET and year(target.gift_date) >= '2016'