我一直在阅读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声明的条款。
答案 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'