テーブルに最後のIDを挿入する最良の方法


35

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

  1. SCOPE_IDENTITY()
  2. 集計関数 MAX()
  3. TOP 1TableNameからのIdentityColumnの選択ORDER BY IdentityColumn DESC

1
postgreSQLを使用すると、シェルフから入手できますpostgresql.org/docs/9.1/static/sql-insert.html
Yevgeniy Afanasyev

Leftfieldオプション-テーブルにGuid列があり、新しいGuidを生成して挿入中に新しい列に挿入できる場合、そのGuidを持つ行を選択して、生成されたint IDを引き出すことができます。
niico

回答:


56

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

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

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

SELECT SCOPE_IDENTITY();

結果:

----
1

OUTPUT複数の行を挿入し、生成されたID のセットを取得する必要がある場合は、句を使用します。

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

結果:

----
2
3

そしてなぜこれが最高の高速オプションなのでしょうか?

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

正確さの側面を無視することは、郵便配達員に今日の郵便物を配達するのに良い仕事をしたことを伝えるようなものです-彼は平均時間よりも10分早くルートを終えました。

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

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

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


私の記憶の中にもう一つ質問がありました。セッションまたはユーザーによって特定のテーブルに生成された最後のIDを取得する必要がある場合、正しい列のMAX()が唯一の正しい方法ですか?
AA.SC

テーブルに複数の行を挿入すると、SCOPE_IDENTITY()は常に最後に生成されたIDを返しますか?列が主キーであり、ID列ではない場合はどうなりますか?
AA.SC

@ AA.SCはい、それは最後のものを返します。ID列でない場合、いいえ、これらの関数はいずれも機能しません。その場合、PK値はどこから来ますか?
アーロンバートランド

私は、カラムがINT型であり、これまでに彼らは新しいレコードを挿入する必要がある場合、開発者はMAX(ColumnNameに)1を使用する我々のアプリケーション内の列のためにこれを見てきました
AA.SC

その後、彼らはすでに挿入した値をすでに知っています。SQL Serverにはそれを伝える方法がありません(トランザクション全体を完全に分離しない限り、パフォーマンスや同時実行に適さない場合を除き、事後にMAXをプルすることに頼ることはできません)。
アーロンバートランド

7

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

SCOPE_IDENTITY()、現在のスコープ内の任意のテーブルに直接挿入された最後のID値を提供します(スコープ=バッチ、ストアドプロシージャなど。ただし、現在のスコープによって起動されたトリガー内などではありません)。

IDENT_CURRENT()どのスコープでも、どのユーザーで特定のテーブルに最後に挿入された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


1
なぜあなたはの使用を奨励しますIDENT_CURRENT()し、@@IDENTITY独自のスクリプトは、彼ら出力誤った結果ことを実証しているとき?
アーロンバートランド

1
@AaronBertrand私が従うかどうかわかりません。生成された最後のID値は8998(ステップが-1であることに注意)であり、それIDENT_CURRENT()が返されます。idは逆方向にカウントするため、MAX()は最初の行を超えて正しい値を返すことはありません。また、IDENTITY_INSERTonの場合、9005は生成された ID値ではないため、に反映されませんIDENT_CURRENT()。しかし、何が返ってくるのを本当に望んでいる場合、「間違った」結果を返す可能性がありSCOPE_IDENTITY()ます。ジョブに適したツールを選択してください。
db2

OPは、挿入したID値の後にあるようです-この場合、8998は正しくありません。あなたが言及するエッジケース(後方インクリメントとIDENTITY_INSERTオン)は、私の意見ではIDENT_CURRENTの使用にさらに反対し、@@ IDENTITYはトリガーの危険性のために実際に使用されるべきではありません(現在または後で追加されます)。OPが使用するIDENT_CURRENTが(特に同時実行の場合)になる理由や、より信頼性の高いメソッドが存在する場合に@@ IDENTITYが使用される理由を理解するのに、まだ苦労しています。
アーロンバートランド

@AaronBertrand目的の結果が現在のスコープからの最後の挿入であるという質問から100%明確ではありません(オプション1はその点で2および3とは異なります)ので、両方とそれらをどのように記述するのが良いアイデアだと思いました異なる。しかし@@IDENTITY、生成されたID値を取得する理想的な方法はほとんどありません。主なポイントは、MAX()またはのTOP 1信頼性の低いバージョンでIDENT_CURRENT()あるということです。これは、それが何をするかを理解している場合に使用するのに最適な関数です。メンテナンスジョブなどに役立ちます。
db2
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.