INSERTステートメントのOUTPUT INTO句でのソース列の使用(SQL Server)


16

私はバッチ処理の挿入ステートメントを書いていて、自分でアイテムをループして挿入された各行に対してSCOPE_IDENTITY()を呼び出す代わりに、一時テーブルを使用して挿入されたIDを追跡したいと考えています。

挿入する必要のあるデータには、(一時的な)IDが他のデータにリンクする(一時的な)IDがあるため、実際のIDと一時的なIDの相互参照が必要です。

これは私がこれまでに持っているものの例です:

-- The existing table 
DECLARE @MyTable TABLE (ID INT IDENTITY(1,1), [Name] NVARCHAR(MAX));

-- My data I want to insert
DECLARE @MyInsertData TABLE (ID INT, [Name] NVARCHAR(MAX));
INSERT INTO @MyInsertData ( ID,Name)
VALUES ( -1 , 'bla'),(-2,'test'),(-3,'last');

DECLARE @MyCrossRef TABLE ([NewId] INT, OldId INT);

INSERT INTO @MyTable ( [Name] )
   OUTPUT Inserted.ID, INS.ID INTO @MyCrossRef
   SELECT [NAME] FROM @MyInsertData INS

-- Check the result
SELECT * FROM @MyCrossRef

問題は、OUTPUT INTO句でIDを受け入れることができないこと@MyInsertData.ID、テーブルを自分自身に結合するその他のトリックを試してみたことですが、何も機能しないようです。

回答:


28

実際に、あなたはを変更することで、同じことを達成することができますINSERTMERGE。このMERGEステートメントは実際にはSQL Serverで「アップサート」を実行するための非常に優れた方法ですが、挿入する目的でのみ使用するのを妨げるものは何もありません。

-- The existing table 
DECLARE @MyTable TABLE (ID INT IDENTITY(1,1), [Name] NVARCHAR(MAX));

-- My data I want to insert
DECLARE @MyInsertData TABLE (ID INT, [Name] NVARCHAR(MAX));
INSERT INTO @MyInsertData ( ID,Name)
VALUES ( -1 , 'bla'),(-2,'test'),(-3,'last');

DECLARE @MyCrossRef TABLE ([NewId] INT, OldId INT);

MERGE INTO @MyTable AS dest
USING @MyInsertData AS ins ON 1=0   -- always false

WHEN NOT MATCHED BY TARGET          -- happens for every row, because 1 is never 0
    THEN INSERT ([Name])
         VALUES (ins.[NAME])

OUTPUT inserted.ID, ins.ID
INTO @MyCrossRef (NewId, OldId);

-- Check the result
SELECT * FROM @MyCrossRef

優れている点の1つは、ソース列だけでなく、句の組み込みテーブルやテーブルにもMERGEアクセスできることです。inserteddeletedOUTPUT

実際にテストしていないため、コードにエラーが含まれている可能性があります。数年前の私のブログ投稿では、クエリのパフォーマンスを含め、もう少し詳しく説明しています。


それは素晴らしいことです!今回はSQL Server 2005のサポートが必要なため、ループに固執する必要があります。ただし、今後のプロジェクトではこのことを覚えておきます。ありがとう!
Louis Somers

3
素晴らしい、これは受け入れられる答えになるはずです。
クリス・ピーコック

3
これがdba stackexchangeではなくstackoverflowにあることを願っています。視認性が低すぎます。素晴らしい答え。
Lordbalmon

3
最初の試みから魅力のように働きました...素晴らしい答え!
BeemerGuy

5

出力句は、ターゲット行と定数/変数のデータにのみアクセスできますSELECT。トリガーで操作している場合のように、ソースの他の場所のデータにはアクセスできません。

https://docs.microsoft.com/en-us/sql/t-sql/queries/output-clause-transact-sqlの状態:

変更するテーブルの列への参照は、INSERTEDまたはDELETEDプレフィックスで修飾する必要があります。

したがって、元のIDを取得するには、次のように出力句がエコーバックできるように、宛先テーブルにIDを含める必要があります。

-- The existing table 
DECLARE @MyTable TABLE (ID INT IDENTITY(1,1), [Name] NVARCHAR(MAX), SourceID INT);

-- My data I want to insert
DECLARE @MyInsertData TABLE (ID INT, [Name] NVARCHAR(MAX));
INSERT INTO @MyInsertData ( ID,Name)
VALUES ( -1 , 'bla'),(-2,'test'),(-3,'last');

DECLARE @MyCrossRef TABLE ([NewId] INT, OldId INT);

INSERT INTO @MyTable ( [Name], SourceID )
   OUTPUT Inserted.ID, Inserted.SourceID INTO @MyCrossRef
   SELECT [NAME], ID FROM @MyInsertData INS

-- Check the result
SELECT * FROM @MyCrossRef

ただし、ターゲットオブジェクトのスキーマを変更することは、状況によっては実用的でない場合があるため、適用できない場合があります。


3
それはかなりがっかりです。キーとして使用することができます第二列:-(ああ、まあ、少なくとも私がしようとして停止することができますし、ループを持つに乗るがない限り、それは私のシナリオの役に立たない出力句をレンダリングする感謝。。
ルイ・ソマーズ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.