【问题标题】:MERGE Query and deleting recordsMERGE 查询和删除记录
【发布时间】:2011-07-09 00:06:30
【问题描述】:

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

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

我有一个接受表值参数的过程,该参数更新与帐户关联的项目。我们将传递如下内容:

AccountID, ItemID
3, 100
3, 200

过程看起来像:

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

我该怎么做?

谢谢,

【问题讨论】:

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


    【解决方案1】:

    我刚刚遇到了@NotMe 提到的相同问题。 @Martin Smith 提到的第二个选项效果很好。 我认为最好的选择是创建一个目标 CTE 来定义具有所有可能条件的目标记录。所以在进行所有的合并操作时,我们不会触及任何不符合我们特定条件的记录

    【讨论】:

    【解决方案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;
    

    结束

    【讨论】:

      【解决方案3】:

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

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

      我遇到的问题是 MERGE 语句 WHEN NOT MATCHED BY SOURCE THEN DELETE 会删除 TARGET 表中的所有内容;不仅仅是 SOURCE 中不再存在的额外项目!我无法限定 WHEN NOT MATCHED BY SOURCE 语句,因此 DELETE 只会影响 TARGET 中不再存在于 SOURCE 中的相同发票编号。

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

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

      【讨论】:

        【解决方案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;
        

        【讨论】:

        • 你能解释一下吗?在不匹配源子句中声明的变量@GroundID在哪里?
        【解决方案5】:

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

        【讨论】:

        • 第一个看起来很有希望。我会试一试,然后告诉你。对于第二个,我想我宁愿运行一个单独的 DELETE 查询而不是执行 CTE。原始查询比上面的示例要复杂一些。
        • 花了一段时间才回到这一点。 DELETE 条件下的第一个选项效果很好。谢谢,
        猜你喜欢
        • 1970-01-01
        • 2016-11-30
        • 2013-09-14
        • 1970-01-01
        • 2019-07-24
        • 2011-11-27
        • 2011-01-10
        • 2022-01-20
        • 1970-01-01
        相关资源
        最近更新 更多