SQL Server-INSERT後の戻り値


318

INSERTステートメントの後にKey-Valueを取得しようとしています。例:nameとid属性を持つテーブルを持っています。idは生成された値です。

    INSERT INTO table (name) VALUES('bob');

次に、同じステップでIDを取得します。これはどのように行われますか?

Microsoft SQL Server 2008を使用しています。


私はここで役に立つ答えが見つかりました:[PreparedStatementの-と文・リターン・生成・キー] [1] [1]:stackoverflow.com/questions/4224228/...
ラースLadegaard

回答:


477

別のSELECTは必要ありません...

INSERT INTO table (name)
OUTPUT Inserted.ID
VALUES('bob');

これは、非IDENTITY列(GUIDなど)でも機能します。


29
もう少し詳しく説明してもらえますか?この例の出力はどこに行きますか? ドキュメントには、テーブルの例のみが示されています(output ... intoを使用)。理想的には、変数にそれを渡すことができるようにしたいのですが
JonnyRaa

2
@JonnyLeeds:(テーブル変数でない限り)変数に対してそれを行うことはできません。OUTPUTはクライアントまたはテーブルに
送られ

7
残念ながら、テーブルにトリガーを追加するとステートメントが壊れるため、これに依存することはできません。re:blogs.msdn.com/b/sqlprogrammability/archive/2008/07/11/…– hajikelist 2015
21:22

1
@hajikelist:これは非常にエッジのあるケースです。トリガーでSET NCOOUNT ONを使用すると通常は役立ちます。stackoverflow.com/questions/1483732/set-nocount-on-usageを
gbn

5
@@ IDENTITYは使用しないでください。SCOPE_IDENTITY、はい、@@ IDENTITYは決してありません。信頼性が低い
gbn 2017年

188

SCOPE_IDENTITY()新しいID値を取得するために使用します

INSERT INTO table (name) VALUES('bob');

SELECT SCOPE_IDENTITY()

http://msdn.microsoft.com/en-us/library/ms190315.aspx


7
idアイデンティティを前提とする
Ilia G

12
@ liho1eye-OPはID列名をidと呼んだため、そうです。
Curt

3
大規模なシステムで、多くのSQLが同時に実行されるとどうなりますか?最後に挿入されたIDをすべてのリクエストに返しますか?
Shiv

2
@Shiv "SCOPE_IDENTITYは現在のスコープ内にのみ挿入された値を返します"
goodies4uall

45
INSERT INTO files (title) VALUES ('whatever'); 
SELECT * FROM files WHERE id = SCOPE_IDENTITY();

トリガーのあるテーブルでのOUTPUT句の競合に関する既知の問題があるため、これが最も安全な方法です。テーブルに現在トリガーがない場合でも、これを非常に信頼性の低いものにします。行の下にトリガーを追加すると、アプリケーションが壊れます。時限爆弾のような行動。

より詳細な説明については、msdnの記事を参照してください。

http://blogs.msdn.com/b/sqlprogrammability/archive/2008/07/11/update-with-output-clause-triggers-and-sqlmoreresults.aspx


トリガーにSET NOCOUNT ONを追加しない場合のみ。docs.microsoft.com/en-us/sql/database-engine/configure-windows/…
gbn

これは、レガシー環境@gbnのオプションではありません
hajikelist

@hajikelist私たちはみなレガシーを持っていますが、トリガーがOUTPUTをめちゃくちゃにするリスクは低く、必要なことは何も設定されていません。誰かがトリガーを追加する場合は、そのコーディング方法を知っている必要があります(主に制御できることを意味します)。または、開発者をトレーニングする必要があります。ある時点で、そのバージョンのSQLがなくなったときに移行を余儀なくされます。サポートされているので、トリガーは結果セットを引き起こしません。何であれ、あなたはINSTEAD OFトリガーがある場合ので、それが最善の答えではないかもしれないSCOPE_IDENTITY作業(stackoverflow.com/questions/908257/...
GBN

@gbn-私はこのような愚かなことを避けたいだけです。「すべてのトリガーに「アプリのステートメントを壊さないでください」を追加することを忘れないでください」とすべての開発者に伝えるつもりはありません。- あなたはそれを維持することができます。「代わりの」シナリオは、はるかにエッジケースのイモです。
ハジケリスト

より安全な答えは、アプリケーションがこのクエリから戻ったときに別のクエリを実行させることです。それがバックエンドで行われている限り、パフォーマンスのペナルティは、人々のグループで開発を管理する単純さの価値があるはずであり、エッジケースを備えたいくつかのクレイジーな機能よりも標準に近いものです。私はむしろエッジケースを私のコードに入れて、プラットフォームでそれらを避けたいです。ただ私の意見はおかしくなりません:)
Dan Chase

33

Entity Frameworkは、GBNの答えに似たものを実行します。

DECLARE @generated_keys table([Id] uniqueidentifier)

INSERT INTO Customers(FirstName)
OUTPUT inserted.CustomerID INTO @generated_keys
VALUES('bob');

SELECT t.[CustomerID]
FROM @generated_keys AS g 
   JOIN dbo.Customers AS t 
   ON g.Id = t.CustomerID
WHERE @@ROWCOUNT > 0

出力結果は一時テーブル変数に格納され、クライアントに返されます。落とし穴に注意する必要があります:

挿入は複数の行を生成できるため、変数は複数の行を保持できるため、複数の行を返すことができます ID

EFが一時テーブルを実際のテーブルに内部結合する理由はわかりません(どのような状況で2つが一致しない場合があります)。

しかし、それがEFの役割です。

SQL Server 2008以降のみ。それが2005年なら、あなたは運が悪い。


2
EFがこれを行う理由は、挿入されたCustomerレコードに対する他のすべての変更も「参照」できるようにするためです。たとえばDEFAULT、一部の列、テーブルのトリガーなどに影響を与える他のDB側ロジックがある場合があります。EFは、エンティティ(オブジェクト)が挿入に使用したため、クライアント側は、IDと、行の現在の状態を表す他のすべてを持つ顧客オブジェクトを取得します。
Hilarion

EFを使用しない別の理由。
cskwg

11

@@IDENTITY 最後に挿入されたID値を返すシステム関数です。


4
@@ IDENTITYを使用しないようにアドバイスする必要があります。これは正確ではない(広すぎる)スレッドセーフではありません。SCOPE_IDENTITY()に関する@Curtの回答を参照してください。
zanlok

10

挿入後に終了するには多くの方法があります

テーブルにデータを挿入する場合、OUTPUT句を使用して、テーブルに挿入されたデータのコピーを返すことができます。OUTPUT句は、OUTPUTとOUTPUT INTOの2つの基本形式を取ります。呼び出し元のアプリケーションにデータを返す場合は、OUTPUTフォームを使用します。データをテーブルまたはテーブル変数に返す場合は、OUTPUT INTOフォームを使用します。

DECLARE @MyTableVar TABLE (id INT,NAME NVARCHAR(50));

INSERT INTO tableName
(
  NAME,....
)OUTPUT INSERTED.id,INSERTED.Name INTO @MyTableVar
VALUES
(
   'test',...
)

IDENT_CURRENT:任意のセッションで特定のテーブルまたはビューに対して作成された最後のIDを返します。

SELECT IDENT_CURRENT('tableName') AS [IDENT_CURRENT]

SCOPE_IDENTITY:同じセッションおよび同じスコープからの最後のIDを返します。スコープは、ストアドプロシージャ/トリガーなどです。

SELECT SCOPE_IDENTITY() AS [SCOPE_IDENTITY];  

@@ IDENTITY:同じセッションの最後のIDを返します。

SELECT @@IDENTITY AS [@@IDENTITY];

1
@RezaJenabi Jun、出力は非常にうまくいき、テーブルで多くのIDを見つけるよりも優れています。に使用out putbulk insertて挿入しましたselect statement。あなたの提案に感謝
アミロホセイン

5

最良かつ最も確実な解決策は SCOPE_IDENTITY()です。

同じスコープで2つの挿入を呼び出すことができるため、挿入するたびにスコープIDを取得して変数に保存する必要があります。

ident_currentそして@@identity、彼らは動作しますが、彼らは安全な範囲ではありませんかもしれません。大きなアプリケーションで問題が発生する可能性があります

  declare @duplicataId int
  select @duplicataId =   (SELECT SCOPE_IDENTITY())

詳細はこちらMicrosoft docs


1
これを単純化できますselect @duplicataId = SCOPE_IDENTITY()
pcnate 2018年

1
OUTPUT節はより良い、より純粋な解決策です:)
Dale K

OUTPUT INTOが非常に遅い。
cskwg


1

挿入コマンドの後に最後に挿入されたIDを取得する方法は複数あります。

  1. @@IDENTITY :テーブルと値を生成したステートメントのスコープに関係なく、現在のセッションの接続で生成された最後のID値を返します
  2. SCOPE_IDENTITY():テーブルに関係なく、現在の接続の現在のスコープにあるinsertステートメントによって生成された最後のID値を返します。
  3. IDENT_CURRENT(‘TABLENAME’):接続、セッション、スコープに関係なく、指定されたテーブルで生成された最後のID値を返します。IDENT_CURRENTはスコープとセッションによって制限されません。指定されたテーブルに限定されます。

どちらが私の要件に完全に一致するかを決定することは、より困難に思えます。

私は主にSCOPE_IDENTITY()を好みます。

insertステートメントでTableNameと共にselect SCOPE_IDENTITY()を使用すると、期待どおりの正確な結果が得られます。

出典:CodoBee


0

これは、SQL ServerでID列としてIDを使用するテーブルに挿入するときに、OUTPUT INSERTEDを使用する方法です。

'myConn is the ADO connection, RS a recordset and ID an integer
Set RS=myConn.Execute("INSERT INTO M2_VOTELIST(PRODUCER_ID,TITLE,TIMEU) OUTPUT INSERTED.ID VALUES ('Gator','Test',GETDATE())")
ID=RS(0)

0

selectステートメントをinsertステートメントに追加できます。整数myInt =テーブル1に挿入(FName)values( 'Fred'); Scope_Identity();を選択します。スケーラーを実行すると、IDの値が返されます。



-4

*接続文字列のパラメーターの順序が重要になる場合があります。*行を追加した後、Providerパラメータの場所によってレコードセットカーソルが壊れる可能性があります。SQLOLEDBプロバイダーでこの動作を確認しました。

行が追加された後は、接続文字列の最初のパラメーターとしてプロバイダーが指定されていない限り、行フィールドは使用できません。プロバイダーが接続文字列の最初のパラメーター以外の場所にある場合、新しく挿入された行フィールドは使用できません。プロバイダーを最初のパラメーターに移動すると、行フィールドが魔法のように表示されました。


1
このコメントが、質問された質問にどのように回答/関連するかを教えてください。大文字/太字に値するとは思えません。回答が役に立ったと思われる場合は、ユーザーが投票します。
n__o

追加したばかりの行を識別するための有効なフィールドがないため、多くのユーザーがこのページにアクセスした可能性があります。私たちが見つけたこの動作(接続文字列のパラメーターの順序を変更するだけで、新しく追加された行にすぐにアクセスできる)は奇妙なので、大文字で言及するのは当然だと思いました。行IDとその行の他のフィールド。プロバイダーを最初のパラメーターとして使用するだけで、問題は解消されます。
David Guidos、2015

回答を編集して改善する必要があります。それは現在騒々しくて、まともな答えとしても、試みとしても出くわしません
James

「うるさい」とはどういう意味ですか?苦情を説明する必要があります。それは可能な限り簡単です。接続文字列のパラメーターの順序を変更すると、挿入後に行データを使用できるかどうかに影響する可能性があります。
David Guidos
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.