2011-03-04 74 views
19

我有一个表,看起来像:MERGE查询和删除记录

AccountID, ItemID 
1, 100 
1, 200 
2, 300 

我有一个接受该更新与帐户相关联的项目的表值参数PROC。我们会通过类似如下:

AccountID, ItemID 
3, 100 
3, 200 

体proc看起来像:

procedure dbo.MyProc(@Items as dbo.ItemListTVP READONLY) 
AS 
BEGIN 
    MERGE INTO myTable as target 
    USING @Items 
     on (Items.AccountId = target.AccountId) 
     AND (Items.ItemId = target.ItemId) 
    WHEN NOT MATCHED BY TARGET THEN 
     INSERT (AccountId, ItemId) 
     VALUES (Items.AccountId, Items.ItemId) 

    ; 

END 
根据传入的数据我希望它2个新记录添加到表,它

我想要的是有一个WHEN NOT MATCHED BY SOURCE子句,它将删除不匹配的指定帐号的项目

例如,如果我通过

AccountID, ItemID 
1, 100 
1, 400 

然后,我希望它删除具有1个,200的纪录;但留下所有其他人。

如果我只是做:

WHEN NOT MATCHED BY SOURCE THEN 
    DELETE; 

然后它会删除所有记录不引用的帐户(即:帐户ID 2和3)。

我该怎么做?

谢谢,

回答

33

我可以想到两种明显的方式,但他们都涉及到再次处理TVP。

首先是简单地改变条件DELETE

WHEN NOT MATCHED BY SOURCE 
    AND target.AccountId IN(SELECT AccountId FROM @Items) THEN 
     DELETE; 

第二是使用CTE来限制目标

WITH cte as 
(
SELECT ItemId, AccountId 
FROM @myTable m 
WHERE EXISTS 
    (SELECT * FROM @Items i WHERE i.AccountId = m.AccountId) 
) 
     MERGE INTO cte as target 
     USING @Items Items 
      ON (Items.AccountId = target.AccountId) AND 
       (Items.ItemId = target.ItemId) 
     WHEN NOT MATCHED BY TARGET THEN 
      INSERT (AccountId, ItemId) 
      VALUES (Items.AccountId, Items.ItemId) 
     WHEN NOT MATCHED BY SOURCE THEN 
      DELETE; 
+0

第一个看起来很有希望,我会给它一个机会让你知道,第二个,我想我宁愿有一个单独的DELETE查询运行而不是执行CTE。原始查询比上面的示例稍微复杂一些。 – NotMe 2011-03-04 18:39:56

+1

花了一段时间回到这个。 DELETE条件上的第一个选项非常完美。谢谢, – NotMe 2011-03-22 15:17:31

+1

First One为我工作。 +1。 – 2016-10-25 02:52:43

4

希望这有助于。

-- myTable 
-- (
--  GroundID bigint, -- FK 
--  GroupID, bigint, -- FK 
--  AcceptingReservations bit 
-- ); 

merge into myTable as target 
using @tmpTable as source 
    on (source.GroundID = target.GroundID) 
    and (source.GroupID = target.GroupID) 
when 
    not matched by target 
    then 
     insert (GroundID, GroupID, AcceptingReservations) 
     values 
     (
      source.GroundID, 
      source.GroupID, 
      source.AcceptingReservations 
     ) 
-- If there is a row that matches, update values; 
when matched 
    then 
     update set 
      target.AcceptingReservations = source.AcceptingReservations 
-- If they do not match, delete for that GroundID only; 
when 
    not matched by source 
    and target.GroundID = @GroundID 
     then 
      delete; 
0

以上答案适用于描述的情况。

我有一个异常表,我用它来存储异常发票。我只希望它包含发票的当前例外情况。因此,如果我在发票数据中修复了一些内容并再次运行该过程,它将创建一个新的例外列表。我希望它添加新的例外情况,更新现有的例外情况,并删除不再存在的例外情况 - 如同它们相同的发票(或任何其他情况)。

我遇到的问题是,如果MERGE语句不符合SOURCE THEN DELETE,则会删除TARGET表中的所有内容;不仅仅是源中的额外物品!我无法限定WHEN NOT MATCHED BY SOURCE语句,因此DELETE只会影响目标中不再来源的相同发票编号。

一个错误告诉我“MERGE语句的'WHEN NOT MATCHED BY SOURCE'子句中只允许使用目标列。“

所以,你必须用一个变量来限定目标行。

3

创建SQL数据库中的表类型变量

CREATE TYPE [dbo].[YourTableType] AS TABLE(
    [AccountID] [int] NULL, 
    [ItemID] [int] NULL 
    ) 
    GO 

,让您的更新程序

ALTER PROCEDURE YourProcedure 
@Items YourTableType READONLY 
AS 
BEGIN 
    MERGE INTO [dbo].[YourTable] as Target 
    USING @Items as Source 
ON 
    Target.[AccountID]=Source.[AccountID] and 
    Target.[ItemID]=Source.[ItemID] 
    WHEN NOT MATCHED by TARGET THEN 
    INSERT 
     ([AccountID], 
     [ItemID]) 
    VALUES 
     (Source.[AccountID], 
     Source.[ItemID]) 

    WHEN NOT MATCHED BY SOURCE AND 
     target.[ItemID] IN(SELECT [ItemID] FROM @Items) 
THEN 
    DELETE; 

END变化