MERGE查询和删除记录

时间:2011-03-04 17:31:45

标签: sql-server tsql merge sql-server-2008-r2

我有一张看起来像这样的表:

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)。

我该怎么做?

谢谢,

4 个答案:

答案 0 :(得分:41)

我可以想到两种显而易见的方法,但两者都涉及再次处理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;

答案 1 :(得分: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;

答案 2 :(得分:2)

在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

答案 3 :(得分:0)

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

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

我遇到的问题是MERGE语句如果没有与源匹配那么删除会删除TARGET表中的所有内容;不只是在SOURCE中不再有额外的物品了!我无法限定WHEN NOT MATCHED BY SOURCE语句,因此DELETE只会影响TARGET中不再在SOURCE中的相同发票号。

错误告诉我“在MERGE声明的'WHEN NOT MATCHED BY SOURCE'子句中只允许使用目标列。”

因此,您必须使用变量限定TARGET行。