web-dev-qa-db-ja.com

テーブルに挿入された最後のIDを取得する最良の方法

挿入によって生成したID値を取得するための最良のオプションはどれですか?パフォーマンスの観点から、これらのステートメントの影響は何ですか?

  1. SCOPE_IDENTITY()
  2. 集計関数MAX()
  3. 選択する TOP 1 IdentityColumn FROM TableName ORDER BY IdentityColumn DESC
38
AA.SC

単一の行を挿入していて、生成されたIDを取得したい場合は、SCOPE_IDENTITY()を使用します。

_CREATE TABLE #a(identity_column INT IDENTITY(1,1), x CHAR(1));

INSERT #a(x) VALUES('a');

SELECT SCOPE_IDENTITY();
_

結果:

_----
1
_

複数の行を挿入していて、以前に使用したIDのsetを取得する必要がある場合は、OUTPUTを使用します生成されました。

_INSERT #a(x) 
  OUTPUT inserted.identity_column 
  VALUES('b'),('c');
_

結果:

_----
2
3
_

なぜこれが最も速いオプションなのですか?

パフォーマンスはさておき、これらは、デフォルトの分離レベルおよび/または複数のユーザーで正しいことが保証されている唯一のものです。正確さの側面を無視しても、SQL ServerはSCOPE_IDENTITY()に挿入された値をメモリに保持するため、当然、これはテーブルまたはシステムテーブルに対して独自の分離クエリを実行して実行するよりも高速です。

正しさの側面を無視することは、今日のメールを配信するのに優れた仕事をしたと郵便配達員に伝えるのと同じです。彼は平均時間よりも10分早くルートを完了しました。問題は、どのメールも適切な家に配達されなかったことです。

次のいずれも使用しないでください

  • _@@IDENTITY_-これはすべてのシナリオで使用できるわけではないため、たとえば、ID列を持つテーブルに、独自のID列を持つ別のテーブルにも挿入するトリガーがある場合、誤った値が返されます。
  • IDENT_CURRENT()-これについて詳しく説明します here であり、コメントも読むのに役立ちますが、基本的に、同時実行では、多くの場合、間違った答えが返されます。
  • MAX()または_TOP 1_-取得できるMAX()が他人のものではないことを確実にするために、2つのステートメントをシリアライズ可能な分離で保護する必要があります。これは単にSCOPE_IDENTITY()を使用するよりもはるかに高価です。

これらの関数は、2つ以上の行を挿入し、すべてのID値を生成する必要がある場合にも失敗します。唯一のオプションは、OUTPUT句です。

57
Aaron Bertrand

パフォーマンスは別として、それらはすべてかなり異なる意味を持っています。

SCOPE_IDENTITY()は、anyテーブルに挿入された最後のID値を提供します現在のスコープ内に直接(スコープ=バッチ、ストアドプロシージャなど。たとえば、現在のスコープによってトリガーされたトリガー)。

IDENT_CURRENT()は、特定のテーブルにanyスコープからanyユーザーが挿入した最後のID値を提供します。

_@@IDENTITY_は、テーブルまたはスコープに関係なく、現在の接続の最新のINSERTステートメントによって生成された最後のID値を提供します。 (補足:Accessはこの関数を使用しているため、ID列を持つテーブルに値を挿入するトリガーに問題があります。)

MAX()または_TOP 1_を使用すると、テーブルに負の識別ステップがある場合、または_SET IDENTITY_INSERT_で行が挿入されている場合に、完全に誤った結果が得られる可能性があります。これらすべてを示すスクリプトは次のとおりです。

_CREATE TABLE ReverseIdent (
    id int IDENTITY(9000,-1) NOT NULL PRIMARY KEY CLUSTERED,
    data char(4)
)

INSERT INTO ReverseIdent (data)
VALUES ('a'), ('b'), ('c')

SELECT * FROM ReverseIdent

SELECT IDENT_CURRENT('ReverseIdent') --8998
SELECT MAX(id) FROM ReverseIdent --9000

SET IDENTITY_INSERT ReverseIdent ON

INSERT INTO ReverseIdent (id, data)
VALUES (9005, 'd')

SET IDENTITY_INSERT ReverseIdent OFF

SELECT IDENT_CURRENT('ReverseIdent') --8998
SELECT MAX(id) FROM ReverseIdent --9005
_

概要:SCOPE_IDENTITY()IDENT_CURRENT()、または_@@IDENTITY_を使用し、実際に必要なものを返すものを使用していることを確認してください。

8
db2