web-dev-qa-db-ja.com

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

私はバッチ処理の挿入ステートメントを書いていて、自分でアイテムをループして挿入された各行に対してSCOPE_IDENTITY()を呼び出す代わりに、一時テーブルを使用して挿入された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と、テーブルをそれ自体に結合する他のトリックを試しましたが、何も機能しないようです。

15
Louis Somers

実際には、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

MERGEの良い点の1つは、ソース列だけでなく、inserted句の組み込みdeletedおよびOUTPUTテーブルにもアクセスできることです。 。

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

27

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

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

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

したがって、元の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

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

5
David Spillett