web-dev-qa-db-ja.com

IDENTITY列に予期しないギャップがあります

1から始まり、1ずつ増加する一意の注文番号を生成しようとしています。次のスクリプトを使用してPONumberテーブルを作成しています。

CREATE TABLE [dbo].[PONumbers]
(
  [PONumberPK] [int] IDENTITY(1,1) NOT NULL,
  [NewPONo] [bit] NOT NULL,
  [DateInserted] [datetime] NOT NULL DEFAULT GETDATE(),
  CONSTRAINT [PONumbersPK] PRIMARY KEY CLUSTERED ([PONumberPK] ASC)    
);

そして、このスクリプトを使用して作成されたストアドプロシージャ:

CREATE PROCEDURE [dbo].[GetPONumber] 
AS
BEGIN
    SET NOCOUNT ON;

    INSERT INTO [dbo].[PONumbers]([NewPONo]) VALUES(1);
    SELECT SCOPE_IDENTITY() AS PONumber;
END

作成時には、これは問題なく機能します。ストアドプロシージャを実行すると、目的の番号から開始され、1ずつ増加します。

奇妙なことに、コンピューターをシャットダウンまたは休止状態にすると、次に手順を実行したときに、シーケンスがほぼ1000進んでいます。

以下の結果を参照してください。

PO Numbers

数値が8から1002に急上昇したことがわかります。

  • なんでこんなことが起こっているの?
  • このように数字がスキップされないようにするにはどうすればよいですか?
  • 私が必要なのは、SQLが次の数値を生成することだけです。
    • a)一意性が保証されています。
    • b)必要な量だけ増分します。

私はSQLの専門家ではないことを認めます。 SCOPE_IDENTITY()の機能を誤解していますか?別のアプローチを使用する必要がありますか? SQL 2012+でシーケンスを調べましたが、Microsoftは、デフォルトでシーケンスが一意であることが保証されていないと述べています。

18
Ege Ersoz

これは既知の予想される問題であり、SQL Server 2012でIDENTITY列がSQL Serverによって管理される方法が変更されました( 背景 );デフォルトでは、1000個の値がキャッシュされます。SQLServerを再起動し、サーバーを再起動し、フェイルオーバーするなどの場合、1000個の値を破棄する必要があります。これは、実際に何個あるかを知るための信頼できる方法がないためです。発行済み。これは文書化されています ここ 。すべてのIDENTITY割り当てがログに記録されるようにこの動作を変更するトレースフラグがあります*。これらの特定のギャップを防止します(ただし、ロールバックや削除によるギャップは防止しません)。ただし、これはパフォーマンスの点で非常にコストがかかる可能性があることに注意することが重要であるため、ここでは特定のトレースフラグについては触れません。

*(個人的には、これは別の方法で解決できる技術的な問題だと思いますが、エンジンを記述していないため、変更することはできません。)

IDENTITYとSEQUENCEがどのように機能するかを明確にするには:

  • どちらも一意であることが保証されていません(主キーまたは一意制約を使用して、テーブルレベルでそれを強制する必要があります)
  • どちらもギャップがないことが保証されていません(たとえば、ロールバックや削除によってギャップが発生しますが、この特定の問題があります)。

一意性は簡単に適用できます。ギャップを回避することはできません。これらのギャップを回避することの重要性を判断する必要があります(理論的には、IDENTITY/SEQUENCE値は無意味な代理キーであるため、ギャップをまったく気にする必要はありません)。それが非常に重要な場合は、どちらの実装も使用するべきではなく、独自のシリアル化可能なシーケンスジェネレーターをロールする必要があります(いくつかのアイデアを参照してください herehere および here )-並行性を無効にすることに注意してください。

この「問題」に関する多くの背景:

25
Aaron Bertrand