IDENTITY
挿入された行を取得する最良の方法は何ですか?
私が知っている@@IDENTITY
とIDENT_CURRENT
とSCOPE_IDENTITY
が、それぞれに付属の長所と短所を理解していません。
誰かが違いを説明してくれませんか、それぞれをいつ使うべきですか?
OUTPUT
SQL Server の句よりも古いものです。
IDENTITY
挿入された行を取得する最良の方法は何ですか?
私が知っている@@IDENTITY
とIDENT_CURRENT
とSCOPE_IDENTITY
が、それぞれに付属の長所と短所を理解していません。
誰かが違いを説明してくれませんか、それぞれをいつ使うべきですか?
OUTPUT
SQL Server の句よりも古いものです。
回答:
@@IDENTITY
現在のセッションの任意のテーブルについて、すべてのスコープにわたって生成された最後のID値を返します。 スコープを超えているため、ここで注意する必要があります。現在のステートメントではなく、トリガーから値を取得できます。
SCOPE_IDENTITY()
現在のセッションおよび現在のスコープ内の任意のテーブルに対して生成された最後のID値を返します。 一般的に何を使いたいか。
IDENT_CURRENT('tableName')
任意のセッションおよびスコープの特定のテーブルに対して生成された最後のID値を返します。これにより、上記の2つが必要なものではない場合(非常にまれ)に、値を取得するテーブルを指定できます。また、@ Guy Starbuckが述べたように、「レコードを挿入していないテーブルの現在のIDENTITY値を取得する場合に使用できます。」
ステートメントのOUTPUT
句を使用すると、そのINSERT
ステートメントを介して挿入されたすべての行にアクセスできます。特定のステートメントにスコープが設定されているため、上記の他の関数よりも簡単です。ただし、これはもう少し冗長で(テーブル変数/一時テーブルに挿入してからクエリする必要があります)、ステートメントがロールバックされるエラーシナリオでも結果が得られます。つまり、クエリが並列実行プランを使用している場合、これがIDを取得するための唯一の保証された方法です(並列処理をオフにする以外)。ただし、トリガーの前に実行され、トリガーによって生成された値を返すために使用することはできません。
output
、あなたは結果を格納する一時テーブルを作成し、クエリを実行する必要はありません。ただ、オフのままinto
出力句の一部を、それが結果セットに出力するでしょう。
OUTPUT
ので、限り、あなたはトリガを使用していないとエラーを処理しているとして、「最高」ですが、SCOPE_IDENTITY
最も簡単であり、非常に稀に問題がある
挿入されたIDを取得する最も安全で正確な方法は、output句を使用することだと思います。
たとえば(次のMSDN記事から引用)
USE AdventureWorks2008R2;
GO
DECLARE @MyTableVar table( NewScrapReasonID smallint,
Name varchar(50),
ModifiedDate datetime);
INSERT Production.ScrapReason
OUTPUT INSERTED.ScrapReasonID, INSERTED.Name, INSERTED.ModifiedDate
INTO @MyTableVar
VALUES (N'Operator error', GETDATE());
--Display the result set of the table variable.
SELECT NewScrapReasonID, Name, ModifiedDate FROM @MyTableVar;
--Display the result set of the table.
SELECT ScrapReasonID, Name, ModifiedDate
FROM Production.ScrapReason;
GO
OUTPUT
があり、SQL Server 2000以前のバージョンのように見えないようです
私は他の人たちと同じことを言っているので、誰もが正しいので、私はそれをより明確にしようとしています。
@@IDENTITY
クライアントのデータベースへの接続によって挿入された最後のもののIDを返します。
ほとんどの場合、これは問題なく機能しますが、トリガーが実行され、わからない新しい行が挿入され、必要な行ではなく、この新しい行からIDが取得される場合があります。
SCOPE_IDENTITY()
この問題を解決します。データベースに送信したSQLコードに最後に挿入したもののIDを返します。トリガーが実行されて余分な行が作成されても、誤った値が返されることはありません。やったー
IDENT_CURRENT
誰かが挿入した最後のIDを返します。他のアプリが偶然に別の行を挿入した場合、その行のIDではなく、その行のIDを取得します。
安全にプレイしたい場合は、常にを使用してくださいSCOPE_IDENTITY()
。あなたがこだわり、@@IDENTITY
後で誰かがトリガーを追加することを決めた場合、すべてのコードが壊れます。
新しく挿入された行のIDを取得する最良の(読み取り:安全)方法は、次のoutput
句を使用することです。
create table TableWithIdentity
( IdentityColumnName int identity(1, 1) not null primary key,
... )
-- type of this table's column must match the type of the
-- identity column of the table you'll be inserting into
declare @IdentityOutput table ( ID int )
insert TableWithIdentity
( ... )
output inserted.IdentityColumnName into @IdentityOutput
values
( ... )
select @IdentityValue = (select ID from @IdentityOutput)
scope_identity()
とにかく単一行の挿入(の最も一般的なケース)が並列プランを取得することは非常にまれです。そして、このバグはこの回答の1年以上前に修正されました。
output
代わりに使用するすべてのSQLを書き換えることでしたscope_identity()
。回答のクラスタリングに関するFUDを削除しました。
Entity Frameworkを使用する場合、内部的にこのOUTPUT
手法を使用して、新しく挿入されたID値を返します
DECLARE @generated_keys table([Id] uniqueidentifier)
INSERT INTO TurboEncabulators(StatorSlots)
OUTPUT inserted.TurboEncabulatorID INTO @generated_keys
VALUES('Malleable logarithmic casing');
SELECT t.[TurboEncabulatorID ]
FROM @generated_keys AS g
JOIN dbo.TurboEncabulators AS t
ON g.Id = t.TurboEncabulatorID
WHERE @@ROWCOUNT > 0
出力結果は一時テーブル変数に格納され、テーブルに結合されて、テーブルから行の値を返します。
注:EFが一時テーブルを実際のテーブルに内部結合する理由はわかりません(どのような状況で2つが一致しない場合があります)。
しかし、それがEFの役割です。
この手法(OUTPUT
)は、SQL Server 2008以降でのみ使用できます。
Entity Frameworkが単にOUTPUT
値を使用するのではなく、元のテーブルに結合する理由は、EFもこの手法を使用しrowversion
て新しく挿入された行を取得するためです。
次のTimestamp
属性を使用 して、エンティティフレームワークモデルで楽観的同時実行を使用できます。🕗
public class TurboEncabulator
{
public String StatorSlots)
[Timestamp]
public byte[] RowVersion { get; set; }
}
これを行うと、Entity Frameworkはrowversion
新しく挿入された行のを必要とします。
DECLARE @generated_keys table([Id] uniqueidentifier)
INSERT INTO TurboEncabulators(StatorSlots)
OUTPUT inserted.TurboEncabulatorID INTO @generated_keys
VALUES('Malleable logarithmic casing');
SELECT t.[TurboEncabulatorID], t.[RowVersion]
FROM @generated_keys AS g
JOIN dbo.TurboEncabulators AS t
ON g.Id = t.TurboEncabulatorID
WHERE @@ROWCOUNT > 0
そして、これを取得するためにTimetsamp
あなたがすることはできません使用OUTPUT
句を。
これは、テーブルにトリガーがあるTimestamp
場合、OUTPUTが間違っているためです。
テーブルにトリガーがある場合、返されるタイムスタンプは正しくありません。したがって、別のを使用する必要がありますSELECT
。
また、不適切な行バージョンに苦しんでも構わないとしても、別の行を実行するもう1つの理由SELECT
はrowversion
、テーブル変数にを出力できないことです。
DECLARE @generated_keys table([Id] uniqueidentifier, [Rowversion] timestamp)
INSERT INTO TurboEncabulators(StatorSlots)
OUTPUT inserted.TurboEncabulatorID, inserted.Rowversion INTO @generated_keys
VALUES('Malleable logarithmic casing');
これを行う3番目の理由は、対称性のためです。UPDATE
トリガーを使用してテーブルに対してを実行する場合、句は使用できませんOUTPUT
。やるしようUPDATE
とすることはOUTPUT
サポートされておらず、エラーになります。
これを行う唯一の方法は、フォローアップSELECT
ステートメントを使用することです。
UPDATE TurboEncabulators
SET StatorSlots = 'Lotus-O deltoid type'
WHERE ((TurboEncabulatorID = 1) AND (RowVersion = 792))
SELECT RowVersion
FROM TurboEncabulators
WHERE @@ROWCOUNT > 0 AND TurboEncabulatorID = 1
TurboEncabulators
:)
@@ IDENTITY、SCOPE_IDENTITY、IDENT_CURRENTは、テーブルのIDENTITY列に挿入された最後の値を返すという点で同様の関数です。
@@ IDENTITYおよびSCOPE_IDENTITYは、現在のセッションの任意のテーブルで生成された最後のID値を返します。ただし、SCOPE_IDENTITYは現在のスコープ内でのみ値を返します。@@ IDENTITYは特定のスコープに限定されません。
IDENT_CURRENTはスコープとセッションによって制限されません。指定されたテーブルに限定されます。IDENT_CURRENTは、任意のセッションおよびスコープの特定のテーブルに対して生成されたID値を返します。詳細については、IDENT_CURRENTを参照してください。
@@ IDENTITYは、現在のSQL接続を使用して挿入された最後のIDです。これは、新しいレコードに挿入されたIDが必要なだけの挿入ストアドプロシージャから戻るのに適した値であり、後でさらに行が追加されたかどうかは気にしません。
SCOPE_IDENTITYは、現在のSQL接続を使用して、現在のスコープに挿入された最後のIDです。つまり、挿入後にトリガーに基づいて2番目のIDENTITYが挿入された場合、SCOPE_IDENTITYには反映されず、実行した挿入のみが反映されます。 。正直なところ、私はこれを使用する理由がありませんでした。
IDENT_CURRENT(テーブル名)は、接続またはスコープに関係なく、最後に挿入されたIDです。これは、レコードを挿入していないテーブルの現在のIDENTITY値を取得する場合に使用できます。
SQL Serverの他のバージョンと話すことはできませんが、2012年には、直接出力することで問題なく動作します。一時テーブルを気にする必要はありません。
INSERT INTO MyTable
OUTPUT INSERTED.ID
VALUES (...)
ちなみに、この手法は複数の行を挿入するときにも機能します。
INSERT INTO MyTable
OUTPUT INSERTED.ID
VALUES
(...),
(...),
(...)
出力
ID
2
3
4
OUTPUT
。一時テーブルが必要ない場合、クエリははるかに単純になります。
常に使用SCOPE_IDENTITY()は、他の何かの必要があることはありません。
を作成し、uuid
それを列に挿入します。次に、uuidを使用して行を簡単に識別できます。これが、実装できる唯一の100%機能するソリューションです。他のすべてのソリューションは複雑すぎるか、同じエッジケースで機能しません。例えば:
1)行を作成する
INSERT INTO table (uuid, name, street, zip)
VALUES ('2f802845-447b-4caa-8783-2086a0a8d437', 'Peter', 'Mainstreet 7', '88888');
2)作成された行を取得する
SELECT * FROM table WHERE uuid='2f802845-447b-4caa-8783-2086a0a8d437';
uuid
データベースにのインデックスを作成することを忘れないでください。したがって、行はより速く検出されます。
https://www.npmjs.com/package/uuid
。 const uuidv4 = require('uuid/v4'); const uuid = uuidv4()
挿入する行のIDを保証するもう1つの方法は、ID値を指定し、SET IDENTITY_INSERT ON
次にを使用することOFF
です。これにより、IDの値が正確にわかるようになります。値が使用されていない限り、これらの値をID列に挿入できます。
CREATE TABLE #foo
(
fooid INT IDENTITY NOT NULL,
fooname VARCHAR(20)
)
SELECT @@Identity AS [@@Identity],
Scope_identity() AS [SCOPE_IDENTITY()],
Ident_current('#Foo') AS [IDENT_CURRENT]
SET IDENTITY_INSERT #foo ON
INSERT INTO #foo
(fooid,
fooname)
VALUES (1,
'one'),
(2,
'Two')
SET IDENTITY_INSERT #foo OFF
SELECT @@Identity AS [@@Identity],
Scope_identity() AS [SCOPE_IDENTITY()],
Ident_current('#Foo') AS [IDENT_CURRENT]
INSERT INTO #foo
(fooname)
VALUES ('Three')
SELECT @@Identity AS [@@Identity],
Scope_identity() AS [SCOPE_IDENTITY()],
Ident_current('#Foo') AS [IDENT_CURRENT]
-- YOU CAN INSERT
SET IDENTITY_INSERT #foo ON
INSERT INTO #foo
(fooid,
fooname)
VALUES (10,
'Ten'),
(11,
'Eleven')
SET IDENTITY_INSERT #foo OFF
SELECT @@Identity AS [@@Identity],
Scope_identity() AS [SCOPE_IDENTITY()],
Ident_current('#Foo') AS [IDENT_CURRENT]
SELECT *
FROM #foo
これは、別のソースからデータをロードしたり、2つのデータベースからデータをマージしたりする場合などに、非常に役立つテクニックです。
これは古いスレッドですが、サーバーを再起動した後のID値のギャップのような、古いバージョンのSQL ServerのIDENTITY列のいくつかの落とし穴を回避する新しい方法があります。シーケンスはSQL Server 2016以降で使用できます。これは、TSQLを使用してSEQUENCEオブジェクトを作成する新しい方法です。これにより、SQL Serverで独自の数値シーケンスオブジェクトを作成し、その増分方法を制御できます。
次に例を示します。
CREATE SEQUENCE CountBy1
START WITH 1
INCREMENT BY 1 ;
GO
次に、TSQLで次のシーケンスIDを取得するには、次のようにします。
SELECT NEXT VALUE FOR CountBy1 AS SequenceID
GO
CREATE SEQUENCEおよびNEXT VALUE FORへのリンクは次のとおりです
Insertステートメントの後に、これを追加する必要があります。そして、データが挿入されているテーブル名について確認してください。挿入ステートメントによって現在影響を受けている行はありません。
IDENT_CURRENT('tableName')
INSERT INTO Table1(fields...) OUTPUT INSERTED.id VALUES (...)
、または古いメソッド:INSERT INTO Table1(fields...) VALUES (...); SELECT SCOPE_IDENTITY();
ExecuteScalar()を使用してc#で取得できます。