web-dev-qa-db-ja.com

UPDATEステートメントのOUTPUTから挿入された行のID値をどのように取得しますか?

挿入された行がOUTPUTステートメントのUPDATEから挿入されたときに、挿入された行のID値をどのように取得しますか? _@@IDENTITY_もSCOPE_IDENTITY()も正しく設定されていないようです。

このコードを考えてみましょう:

_DECLARE @UpdateTable table (UpdateTableID int IDENTITY, UpdateTableValue int);
DECLARE @InsertTable table (InsertTableID int IDENTITY, UpdateTableValue1 int, UpdateTableValue2 int);
DECLARE @TestValue int = 5;
INSERT INTO @UpdateTable (UpdateTableValue) VALUES (1),(2),(3);
SELECT [@@IDENTITY] = @@IDENTITY, [SCOPE_IDENTITY()] = SCOPE_IDENTITY();

INSERT INTO @InsertTable (UpdateTableValue1, UpdateTableValue2)
SELECT
    UpdateTableValue1, UpdateTableValue2
FROM        (
                UPDATE      @UpdateTable
                SET         UpdateTableValue = UpdateTableValue + @TestValue
                OUTPUT      deleted.UpdateTableValue, inserted.UpdateTableValue
                WHERE       UpdateTableID = 2
            ) AS UpdateResults (UpdateTableValue1, UpdateTableValue2);

SELECT [@@IDENTITY] = @@IDENTITY, [SCOPE_IDENTITY()] = SCOPE_IDENTITY();
_

最後に挿入された行のID値は1ですが、_@@IDENTITY_およびSCOPE_IDENTITY()関数は、最後のステートメントが実行される前の元のINSERTから元の値を返します。

_@@VERSION_:

Microsoft SQL Azure(RTM)-12.0.2000.8 May 2 2019 20:11:13 Copyright(C)2019 Microsoft Corporation

2
Riley Major

これは、_@@IDENTITY_およびSCOPE_IDENTITY()UPDATEステートメントと組み合わせて使用​​すると機能しないためと考えられます。これに対する自然な応答は「どうして外側のINSERTのIDを返さないのですか?」答えは、スコープのためです。

UPDATEステートメントとINSERTステートメント(OUTPUTの結果を使用)は1つのステートメントとして実行され、本質的に互いに同じスコープ内にあります。エンジンがクエリを実行すると、これが認識されるため、外部のINSERTによって生成されたIDを追跡できません。これを証明するには、UPDATEINSERTを2つの個別のステートメントに分割する次のクエリを実行します。

_DECLARE @UpdateTable table 
(
    UpdateTableID int IDENTITY, 
    UpdateTableValue int
);
DECLARE @InsertTable table 
(
    InsertTableID int IDENTITY, 
    UpdateTableValue1 int, 
    UpdateTableValue2 int
);

DECLARE @TestValue int = 5;

INSERT INTO @UpdateTable (UpdateTableValue) 
VALUES (1),(2),(3);

SELECT [@@IDENTITY] = @@IDENTITY, [SCOPE_IDENTITY()] = SCOPE_IDENTITY();

DECLARE @tmp TABLE 
(
    UpdateTableValue1 int, 
    UpdateTableValue2 int
);

UPDATE      @UpdateTable
SET         UpdateTableValue = UpdateTableValue + @TestValue
OUTPUT      deleted.UpdateTableValue, inserted.UpdateTableValue
INTO        @tmp
WHERE       UpdateTableID = 2;

INSERT INTO @InsertTable (UpdateTableValue1, UpdateTableValue2)
SELECT
    UpdateTableValue1, UpdateTableValue2
FROM        @tmp;

SELECT [@@IDENTITY] = @@IDENTITY, [SCOPE_IDENTITY()] = SCOPE_IDENTITY();
_

正しい結果が得られます:

enter image description here

テーブルからIDを取得する必要がある場合、私が見る限り、3つのオプションがあります。

  1. 上記で使用した手法を使用して値を取得します。
  2. OUTPUT句を最後のINSERTに追加して、生成されたIDを取得します。
  3. 現在の値を取得するには、IDENT_CURRENT()関数を使用します。

(グローバル一時テーブルではなく)一時テーブルを使用している場合、IDENT_CURRENT()は、セッション内で生成された値のみを返すことを保証できるため、完全に問題ありません(他のセッションではオブジェクトを使用できないため)。 。ただし、指定した例がそれだけの場合は、オプション2を使用して、別のOUTPUT句を使用して、生成されたID値をキャプチャします。

_INSERT INTO @InsertTable (UpdateTableValue1, UpdateTableValue2)
OUTPUT inserted.InsertTableID
SELECT
    UpdateTableValue1, UpdateTableValue2
FROM        
(
    UPDATE      @UpdateTable
    SET         UpdateTableValue = UpdateTableValue + @TestValue
    OUTPUT      deleted.UpdateTableValue, inserted.UpdateTableValue
    WHERE       UpdateTableID = 2
) AS UpdateResults (UpdateTableValue1, UpdateTableValue2);
_

次に、正しい値を出力します。

enter image description here

2
Mr.Brownstone