web-dev-qa-db-ja.com

MERGEクエリとレコードの削除

次のようなテーブルがあります。

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)のすべてのレコードを削除します。

これどうやってするの?

おかげで、

28
NotMe

私は2つの明白な方法を考えることができますが、どちらもTVPを再度処理する必要があります。

最初は単にDELETE条件を変更することです

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

2つ目は、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;
42
Martin Smith

お役に立てれば。

--  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;
5
dbaourdos

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;

終わり

2

上記の回答は、説明されている状況で機能します。

請求書の例外を保存するために使用する例外テーブルがあります。請求書の現在の例外のみを含めます。したがって、請求書データの一部を修正してプロセスを再度実行すると、例外の新しいリストが作成されます。新しい例外を追加し、既存の例外を更新して、もう存在しない例外を削除します-SO同じ請求書に含まれている限り(または何でも)。

私が抱えていた問題は、MERGEステートメントがSOURCE THEN DELETEによって一致しないと、TARGETテーブル内のすべてが削除されることでした。 SOURCEに存在しない追加アイテムだけではありません! WHEN NOT MATCHED BY SOURCEステートメントを修飾できなかったので、DELETEは、SOURCEに存在しなくなったTARGETの同じ請求書番号のみに影響します。

「MERGEステートメントの「WHEN NOT MATCHED BY SOURCE」句ではターゲット列のみが許可されています。」というエラーが表示されました。

したがって、TARGET行を変数で修飾する必要があります。

0
Scott