web-dev-qa-db-ja.com

SQL Serverから次のID値を取得する方法

SQL Serverから次のID値を取得する必要があります。

私はこのコードを使用します:

SELECT IDENT_CURRENT('table_name') + 1

これは正しいですが、table_nameが空(および次のID値が "1")の場合、 "2"を返しましたが、結果は "1"

23
Mehdi Esmaeili

次の利用可能な値を計算する別の方法を探したいと思うと思います( 列を自動インクリメントに設定する など)。

空のテーブルに関する IDENT_CURRENT ドキュメントから:

IDENT_CURRENT値がNULLの場合(テーブルに行が含まれていないか、切り捨てられていないため)、IDENT_CURRENT関数はシード値を返します。

特に、複数の人が同時にテーブルに書き込むアプリを設計することになった場合は、それほど信頼できるとは思えません。

IDENT_CURRENTを使用して、次に生成されるID値を予測することに注意してください。実際に生成される値は、他のセッションによって実行される挿入のため、IDENT_CURRENTとIDENT_INCRが異なる場合があります。

11
Grant

テーブルが空になる場合、このクエリは完全に機能します。

SELECT
  CASE
    WHEN (SELECT
        COUNT(1)
      FROM tablename) = 0 THEN 1
    ELSE IDENT_CURRENT('tablename') + 1
  END AS Current_Identity;
6
Abhijit Gosavi

私はこれがこれを行う正しい方法ではないことを他のポスターに同意する傾向がありますが、特定の場合には便利です。いくつかの投稿では、なぜこれを行うのかと尋ねられ、それが私にとって都合の良い例と、その方法と理由を説明します。

ビットコインノードを実装しています。ブロックチェーンをSQLデータベースに保存したい。すべてのブロックは、他のノードおよびマイナーからネットワークから受信されます。他の場所で見つけることができる詳細。

ブロックを受信すると、1つのヘッダー、任意の数のトランザクション、および各トランザクションの任意の数の入力と出力が含まれます。データベースには4つのテーブルがあります-ご想像のとおり-ヘッダーテーブル、トランザクションテーブル、入力テーブル、出力テーブル。トランザクション、入力、および出力テーブルの各行は、ヘッダー行まで相互にIDでリンクされています。

一部のブロックには、数千のトランザクションが含まれています。一部のトランザクションには、入力および/または出力があります。整合性を損なうことなく、C#での便利な呼び出しからIDをDBに保存する必要があります(IDはすべてリンクします)。

代わりに、操作中にC#でデータベースオブジェクトを同期ロックするようにします(そして、他のプロセスがデータベースにアクセスすることを心配する必要もありません)。したがって、4つのテーブルすべてでIDENT_CURRENTを便利に実行できます。ストアドプロシージャからの値、IDをインクリメントしながら4つのList <DBTableRow>のほぼ10000行を埋め、オプションSqlBulkCopyOptions.KeepIdentityを設定してSqlBulkCopy.WriteToServerメソッドを呼び出し、すべてをテーブルセットごとに1つの4つの単純な呼び出しで送信します。

(4〜5歳のミッドレンジラップトップでの)パフォーマンスの向上は、非常に大きなブロックで約60〜90秒から2〜3秒に下がっていたため、IDENT_CURRENT()について学ぶことができました。

解決策はエレガントではないかもしれませんし、いわば本によってではないかもしれませんが、それは便利でシンプルです。これを達成する他の方法もありますが、これは簡単で、実装に数時間かかりました。並行性の問題がないことを確認してください。

5
Søren L. Fog

私はすでに答えがあることを知っていますが、「次のアイデンティティSQLサーバーを取得」の行に沿ったすべての検索が不安定なソリューション(単に現在のアイデンティティ値を選択して1を追加するなど)または「できる」確実に行われます」。

実際にこれを行うには、いくつかの方法があります。

SQL Server> = 2012

_CREATE SEQUENCE dbo.seq_FooId START WITH 1 INCREMENT BY 1
GO

CREATE TABLE dbo.Foos (
    FooId int NOT NULL 
        DEFAULT (NEXT VALUE FOR dbo.seq_FooId)
        PRIMARY KEY CLUSTERED 
)
GO

// Get the next identity before an insert
DECLARE @next_id = NEXT VALUE FOR dbo.seq_FooId
_

SQL Server 2012では、SEQUENCEオブジェクトが導入されました。この場合、シーケンスは_NEXT VALUE FOR_が呼び出されるたびにインクリメントされるため、同時実行性について心配する必要はありません。

SQL Server <= 2008

_CREATE TABLE dbo.Foos (
    FooId int NOT NULL 
        IDENTITY (1, 1)
        PRIMARY KEY CLUSTERED 
)
GO

// Get the next identity before an insert
BEGIN TRANSACTION
SELECT TOP 1 1 FROM dbo.Foos WITH (TABLOCKX, HOLDLOCK)
DECLARE @next_id int = IDENT_CURRENT('dbo.Foos') + IDENT_INCR('dbo.Foos');
DBCC CHECKIDENT('dbo.Foos', RESEED, @next_id)
COMMIT TRANSACTION
_

特にDBCCステートメントには昇格されたアクセスが必要であり、おそらくすべての人がそのようなアクセスを持つことは望ましくないので、おそらくすべてをストアドプロシージャにカプセル化する必要があります。

_NEXT VALUE FOR_ほどエレガントではありませんが、信頼できるはずです。テーブルに行がない場合、最初の値として_2_を取得しますが、常にこのメソッドを使用して次のIDを取得する場合は、代わりに_0_でIDをシードできます。 1_(IDENTITY (0, 1)を使用)の場合、1から開始するように設定されていない場合.

なぜ誰もがこれをしたいのですか?

私は質問のポスターについて話すことはできませんが、本「ドメイン駆動設計」と 「公式」DDDサンプルはこの手法を使用しています (または少なくともヒント)エンティティが常に持っていることを強制する方法として有効な識別子。エンティティが偽の識別子(_-1_またはdefault(int)またはnullなど)をデータベースにINSERTedされるまで持っている場合、永続性の懸念が漏れている可能性があります。

3
tuespetre
SELECT isnull(IDENT_CURRENT('emp') + IDENT_INCR('emp'),1)
2
Islam Gx