SQL Serverでレコードを削除した後にIDシードをリセットする


682

SQL Serverデータベーステーブルにレコードを挿入しました。テーブルには主キーが定義されており、自動インクリメントIDシードは「はい」に設定されています。これは主に、SQL Azureでは、各テーブルに主キーとIDを定義する必要があるためです。

しかし、テーブルからいくつかのレコードを削除する必要があるため、それらのテーブルのIDシードが乱れ、インデックス列(1の増分で自動生成されます)が乱されます。

列を昇順の番号順にするためにレコードを削除した後、ID列をリセットするにはどうすればよいですか?

ID列は、データベースのどこでも外部キーとして使用されません。


4
"SQL Azureで"-"各テーブルに主キーが必要"-true-"およびIdentity Defined"-false。IDと主キーは直交する概念です。ID列は、テーブルのPKである必要はありません。主キーはID列である必要はありません。
Damien_The_Unbeliever 2014

OK。私のコンセプトは間違っているかもしれません。しかし、今私はPKとIdentity Seedでテーブル構造を定義しました。一部の行を削除する必要がある場合、Identity Seedを正しい数値の昇順でリセットするにはどうすればよいですか
xorpower

29
ID列で生成された実際の数値を気にかけると、それらを誤用していると私は常に主張します。ID列で注意する必要があるのは、一意の値が自動的に生成され(yay!)、これらの値を数値列に格納できることです(このビットは、これらの値を保持する列の宣言にのみ関連します)。あなたはそれらを誰にも見せてはならないので、彼らがどのような値をとるかは問題ではありません。
Damien_The_Unbeliever 14

他に言及されているようにdbccチェック識別を使用できますが、SQL db v12の主キーは必須ではないことに注意してください
Satya_MSFT

回答:


1099

DBCC CHECKIDENT管理コマンドは、IDカウンタをリセットするために使用されます。コマンド構文は次のとおりです。

DBCC CHECKIDENT (table_name [, { NORESEED | { RESEED [, new_reseed_value ]}}])
[ WITH NO_INFOMSGS ]

例:

DBCC CHECKIDENT ('[TestTable]', RESEED, 0);
GO

以前のバージョンのAzure SQL Databaseではサポートされていませんでしたが、現在はサポートされています。


new_reseed_value引数は、ドキュメントに従って SQL Serverのバージョンによって異なることに注意してください。

テーブルに行が存在する場合、次の行がnew_reseed_value値とともに挿入されます。SQL Server 2008 R2以前のバージョンでは、挿入された次の行はnew_reseed_value +現在の増分値を使用します。

ただし、観察された動作は少なくともSQL Server 2012 がまだnew_reseed_value +現在の増分値ロジックを使用していることを示しているため、この情報は誤解を招く(実際にはまったく間違っている)とわかります。Microsoft Example Cは、同じページにある独自のものとも矛盾しています。

C.現在のID値を新しい値に強制する

次の例では、AddressTypeテーブルのAddressTypeID列の現在のID値を10の値に強制します。テーブルには既存の行があるため、挿入された次の行は値として11を使用します。つまり、列の値+ 1。

USE AdventureWorks2012;  
GO  
DBCC CHECKIDENT ('Person.AddressType', RESEED, 10);  
GO

それでも、これはすべて、新しいSQL Serverバージョンでの異なる動作のオプションを残します。私が確信する唯一の方法は、Microsoftが自身のドキュメンテーションで物事を明確にするまでは、使用前に実際のテストを行うことだと思います。


23
構文は次のようになります... DBCC CHECKIDENT( '[のTestTable]'、RESEED、0)GO
Biki

2
それが表示されますDBCC CHECKIDENT:次期リリース(V12 /スターリング)でサポートされてazure.microsoft.com/en-us/documentation/articles/...けれどもが、この特定の状況のために、私はまだ推薦TRUNCATE TABLEを :)
ソロモンRutzky

1
「GO」が別のラインになるまで、私にはうまくいきませんでした。
mrówa

1
同じ行にGOキーワードがあるため、構文にフラグが付けられていますが、理由はわかりません。あなたはそれを一線下に動かすことができますか?この行を50回コピーして貼り付けたので、戻って修正する必要があります。
AnotherDeveloper 2016年

4
私には完璧に働きました。それの価値は、あなたの最初のレコードは、ID 1になるように再シードしたい場合は、その後再シードコマンドは次のレコードがID 1であるように、0に再シードする必要があり、テーブルを再播種時に指摘
マイク・アップジョン

215
DBCC CHECKIDENT ('TestTable', RESEED, 0)
GO

ここで、0はidentity開始値です


15
だけを呼び出した場合など、テーブルが空の場合、TRUNCATE新しいシード値は次に使用する値(つまり、0ではなく1)にする必要があります。テーブルが空でない場合は、を使用しnew_reseed_value + 1ます。MSDN
kjbartel 2015年

2
@kjbartel、Anil、その他:「テーブルが空の場合」ほど単純ではありません。が原因でテーブルが空である場合の説明がドキュメントにDELETEありませんでした。TRUNCATEその場合もそうnew_reseed+value + 1です。私はこれについての投稿を書いて、いくつかのテストを通じて実際の動作を示し、実際のドキュメントを更新しました(GitHubにあるため、これが可能になりました)。
ソロモン・ルツキー

87

IFことに留意すべき全てのデータが経由してテーブルから削除されているDELETE(すなわちノーWHEREであるように思われる(表を参照ないFKSが存在しない)、その後限り)アクセス許可が、aとbを可能にするように、句)ここでのケース)、TRUNCATE TABLEより効率的でDELETE ありIDENTITY、同時にシードをリセットするため、使用することをお勧めします 次の詳細は、TRUNCATE TABLEの MSDNページからの抜粋です。

DELETEステートメントと比較して、TRUNCATE TABLEには次の利点があります。

  • 使用されるトランザクションログ領域が少なくなります。

    DELETEステートメントは、一度に1行ずつ削除し、削除された各行のエントリをトランザクションログに記録します。TRUNCATE TABLEは、テーブルデータの格納に使用されるデータページの割り当てを解除することでデータを削除し、ページの割り当て解除のみをトランザクションログに記録します。

  • 通常、使用されるロックは少なくなります。

    行ロックを使用してDELETEステートメントを実行すると、テーブルの各行が削除のためにロックされます。TRUNCATE TABLEは常にテーブル(スキーマ(SCH-M)ロックを含む)とページをロックしますが、各行はロックしません。

  • 例外なく、テーブルにはページが残りません。

    DELETEステートメントが実行された後も、テーブルに空のページが含まれることがあります。たとえば、ヒープ内の空のページは、少なくとも排他的な(LCK_M_X)テーブルロックがないと割り当て解除できません。削除操作でテーブルロックを使用しない場合、テーブル(ヒープ)には多くの空のページが含まれます。インデックスの場合、削除操作によって空のページが残る場合がありますが、これらのページはバックグラウンドのクリーンアッププロセスによってすばやく割り当て解除されます。

テーブルにID列が含まれている場合、その列のカウンターは、列に定義されているシード値にリセットされます。シードが定義されていない場合、デフォルト値1が使用されます。IDカウンターを保持するには、代わりにDELETEを使用します。

したがって、次のようになります。

DELETE FROM [MyTable];
DBCC CHECKIDENT ('[MyTable]', RESEED, 0);

ちょうどなる:

TRUNCATE TABLE [MyTable];

TRUNCATE TABLE制限事項などの詳細については、上記のリンクのドキュメントを参照してください。


8
正しい状況ではより効率的ですが、これは常にオプションとは限りません。Truncateは、FKが定義されているテーブルでは実行されません。依存するレコードがない場合でも、制約が存在すると切り捨ては失敗します。また、トランケートには、削除のみが削除を必要とするALTER権限が必要です。
Rozwel

3
@Rozwel真実ですが、適切な権限を設定する必要があることを述べて、回答をすでに修飾していました。また、質問にはFKがないことが具体的に示されています。ただし、明確にするために、「FKなし」の制限を指定するように更新しました。ご指摘いただきありがとうございます。
ソロモンルツキー

1
唯一の問題は、FKが切り捨てをブロックすることです。PK列またはID列の一部ではない一意の制約に対してFKを設定することは(まれではありますが)可能です。
Rozwel

1
@Rozwelこれも真実ですが、Azure SQLデータベースで必要とされるOPの理解(正しいかどうか)のためにPKのみが存在することを考えると、一意の制約がないという質問から推測するのは妥当なようです。とにかく、私は曖昧さを減らすために皆ですので、私は再び更新しました。ありがとう。
ソロモンルツキー、2015年

テーブルに外部キーがあることはそれほど珍しいことではなく、外部キーが存在するとTRUNCATE TABLEが禁止されます。テーブル内の他の2つの列と外部テーブル内の一意のインデックスに対して適用される外部キーを持つテーブルでTRUNCATE TABLEを実行しようとしたとき、私はこれを困難な方法で発見しました。
デビッドA.グレイ

83

ほとんどの回答はRESEEDを0に提案していますが、多くの場合、利用可能な次のIDに再シードする必要があります

declare @max int
select @max=max([Id])from [TestTable]
if @max IS NULL   //check when max is returned as null
  SET @max = 0
DBCC CHECKIDENT ('[TestTable]', RESEED,@max)

これにより、テーブルがチェックされ、次のIDにリセットされます。


2
これは、100%
リバースエンジニア

3
少し短い:declare @max int select @max=ISNULL(max([Id]),0) from [TestTable]; DBCC CHECKIDENT ('[TestTable]', RESEED, @max );
ギジェルモプランディ

61

私が@anil shahs答えてみたところ、IDがリセットされました。しかし、新しい行が挿入されたとき、それはを取得しましたidentity = 2。代わりに、構文を次のように変更しました:

DELETE FROM [TestTable]

DBCC CHECKIDENT ('[TestTable]', RESEED, 0)
GO

次に、最初の行はID = 1を取得します。



16

ほとんどの回答はに示唆RESEEDしていますが0、一部の回答ではこれをTRUNCATEDテーブルの欠陥と見なしていますが、Microsoftには、ID

DBCC CHECKIDENT ('[TestTable]', RESEED)

これにより、テーブルがチェックされ、次のテーブルにリセットされIDます。これは、MS SQL 2005から現在まで利用可能です。

https://msdn.microsoft.com/en-us/library/ms176057.aspx


1
残念ながらそれは真実ではありません。MS SQL 2014サーバー用にチェックしたところです。
alehro

1
実際、SQL 2014にも当てはまります。テストしたところ、うまくいきました。
Daniel Dyson

2
これはSQL 2012で私にとって一貫性のない動作をします。予想通りに次に利用可能なものを使用することもあれば、テーブルからの古い値でスタックするように見えることもあります。種子の指定は常に機能します。
Dan Field

SQL 2016では私には機能しません-アイデンティティシードをそのままにします。一度は正常に機能したかもしれませんが、指の問題かもしれません。再度機能させることができない
リバースエンジニア、

メッセージは成功を示しますChecking identity information: current identity value '[incorrect seed]', current column value '[correct seed]'.が、新しい挿入時には、まだ誤ったシードが使用されています。
Denziloe

7

2つのコマンドを発行すると、トリックを行うことができます

DBCC CHECKIDENT ('[TestTable]', RESEED,0)
DBCC CHECKIDENT ('[TestTable]', RESEED)

最初はIDをゼロにリセットし、次はIDを次に使用可能な値に設定します-jacob


2
DBCC CHECKIDENT( '[TestTable]'、RESEED)が次に使用可能な値に再シードされない
Atal Kishore

これは、オプション「Reseed identity columns」がオンになっているときにRedGate Data Compareによって使用される方法です。私はそれを広範囲にテストしました(つまり、RedGateツールではなくSQLコードで)、そして確実に動作します。(私はRedGateとは関係ありませんが、試用版を時々使用しているだけです)
エンジニア

6

@jacob

DBCC CHECKIDENT ('[TestTable]', RESEED,0)
DBCC CHECKIDENT ('[TestTable]', RESEED)

私のために働いた、私は最初にすべてのエントリをテーブルからクリアする必要があり、それから削除後のトリガーポイントに上記を追加した。今、私はエントリを削除するたびにそこから取得されます。


DBCC CHECKIDENTは削除後にのみ機能します。切り捨てを使用することもできます。ただし、残りのデータが必要な場合は使用しないでください。また、truncateは、削除されたレコードのレコードカウントを提供しません。
user763539 2017年

6

Truncate テーブルは、レコードをクリアし、カウンターをリセットし、ディスク領域を再利用するため、推奨されます。

DeleteまたCheckIdent、外部キーによって切り捨てられない場合にのみ使用してください。


5

ID列を新しいIDでリセット...

DECLARE @MAX INT
SELECT @MAX=ISNULL(MAX(Id),0) FROM [TestTable]

DBCC CHECKIDENT ('[TestTable]', RESEED,@MAX)

4

これは一般的な質問であり、答えは常に同じです:しないでください。ID値は任意として扱う必要があるため、「正しい」順序はありません。


15
これは本番環境にも当てはまりますが、開発中、特定のエンティティにはシードスクリプトから入力された特定のIDがあることを覚えておきたいと思います。これにより、開発中のデータベースのナビゲートがはるかに簡単になります。
Francois Botha 2014年

7
このような回答は完全に理論的なものであり、現実のニーズを満たすことはめったにありません。あなたのドグマで人々を洗脳する代わりに、あなたはOPの質問に答えます...
Serj Sagan 2015

1
クールな話、仲間。私の主張はこれです:列の値を指定したい場合、それを困難にする列のプロパティを選択しないでください。コードのにおいはこれです:テーブルにレコードを挿入するたびにID列の値を指定すると、ID列がなくなります。IDの重要なポイントは、サーバーに値を作成させることです。したがって、これまでの時間をオーバーライドすると、ゼロ以外のコストで何も得られません。また、アドホミネムの議論については良い仕事です。
Ben Thul 2015年

5
私は確かにあなたの主張に同意します。額面を見ると、OPは間違いを犯していることは確かですが、おそらくOPが質問に回答してもらうのに適切であるとは考えていなかったという投稿には、より深い必要性がありません。したがって、質問に答え、答えの一部として「すべきこと」と「すべきでないこと」のアドバイスを与えます。ちなみに、私はあなたのキャラクターを攻撃したことはありません...
アドホミネム

1
ほとんどの場合は確かに当てはまりますが、テーブルを再シードすることが正当な場合もあります。たとえば、私はグリーンフィールドプロジェクトに取り組んでいます。このプロジェクトは、置き換えられる前任者の既存の行を説明するために、特定のポイントから開始する必要があります。開発中の再シードは、正当なユースケースであるIMOです。
デビッドA.グレイ

3

このスクリプトを実行してID列をリセットします。2つの変更を行う必要があります。tableXYZを、更新する必要のある任意のテーブルに置き換えます。また、ID列の名前を一時テーブルから削除する必要があります。これは、35,000行&3列のテーブルで瞬時に発生しました。明らかに、テーブルをバックアップし、最初にこれをテスト環境で試します。


select * 
into #temp
From tableXYZ

set identity_insert tableXYZ ON

truncate table tableXYZ

alter table #temp drop column (nameOfIdentityColumn)

set identity_insert tableXYZ OFF

insert into tableXYZ
select * from #temp

3
これは完全に正しいわけではありません。SETIDENTITY_INSERTが間違った場所にあります。これはTRUNCATEを回避するのではなく、INSERT INTO(したがってidentity_ INSERT)を回避します。また、これはデータを保持する必要がある場合にのみ使用します。それ以外の場合は、単一のTRUNCATEステートメントを実行するだけの場合に比べて非常に非効率的です。
ソロモンRutzky

1
DBCC CHECKIDENT (<TableName>, reseed, 0)

これにより、現在のID値が0に設定されます。

次の値を挿入すると、ID値が1に増加します。


1

このストアドプロシージャを使用します。

IF (object_id('[dbo].[pResetIdentityField]') IS NULL)
  BEGIN
    EXEC('CREATE PROCEDURE [dbo].[pResetIdentityField] AS SELECT 1 FROM DUMMY');
  END
GO

SET  ANSI_NULLS ON
GO
SET  QUOTED_IDENTIFIER ON
GO

ALTER PROCEDURE [dbo].[pResetIdentityField]
  @pSchemaName NVARCHAR(1000)
, @pTableName NVARCHAR(1000) AS
DECLARE @max   INT;
DECLARE @fullTableName   NVARCHAR(2000) = @pSchemaName + '.' + @pTableName;

DECLARE @identityColumn   NVARCHAR(1000);

SELECT @identityColumn = c.[name]
FROM sys.tables t
     INNER JOIN sys.schemas s ON t.[schema_id] = s.[schema_id]
     INNER JOIN sys.columns c ON c.[object_id] = t.[object_id]
WHERE     c.is_identity = 1
      AND t.name = @pTableName
      AND s.[name] = @pSchemaName

IF @identityColumn IS NULL
  BEGIN
    RAISERROR(
      'One of the following is true: 1. the table you specified doesn''t have an identity field, 2. you specified an invalid schema, 3. you specified an invalid table'
    , 16
    , 1);
    RETURN;
  END;

DECLARE @sqlString   NVARCHAR(MAX) = N'SELECT @maxOut = max(' + @identityColumn + ') FROM ' + @fullTableName;

EXECUTE sp_executesql @stmt = @sqlString, @params = N'@maxOut int OUTPUT', @maxOut = @max OUTPUT

IF @max IS NULL
  SET @max = 0

print(@max)

DBCC CHECKIDENT (@fullTableName, RESEED, @max)
go

--exec pResetIdentityField 'dbo', 'Table'

ちょうど私の答えを再訪します。SQL Server 2008 R2で気が付くべき奇妙な動作に遭遇しました。

drop table test01

create table test01 (Id int identity(1,1), descr nvarchar(10))

execute pResetIdentityField 'dbo', 'test01'

insert into test01 (descr) values('Item 1')

select * from test01

delete from test01

execute pResetIdentityField 'dbo', 'test01'

insert into test01 (descr) values('Item 1')

select * from test01

最初の選択はを生成し0, Item 1ます。

2つ目はを生成し1, Item 1ます。テーブルが作成された直後にリセットを実行すると、次の値は0になります。正直なところ、Microsoftがこれを正しく行えないことに驚いていません。テーブルを再作成した後で実行したり、テーブルが既に作成されているときに実行したりする参照テーブルを作成するスクリプトファイルがあるので、それを発見しました。


1

これを行うには、次のスクリプトを使用します。「エラー」が発生するシナリオは1つだけですIDENT_CURRENT。つまり、テーブルからすべての行を削除し、現在1に設定されている場合です。つまり、最初はテーブルに1行しかありませんでした。

DECLARE @maxID int = (SELECT MAX(ID) FROM dbo.Tbl)
;

IF @maxID IS NULL
    IF (SELECT IDENT_CURRENT('dbo.Tbl')) > 1
        DBCC CHECKIDENT ('dbo.Tbl', RESEED, 0)
    ELSE
        DBCC CHECKIDENT ('dbo.Tbl', RESEED, 1)
    ;
ELSE
    DBCC CHECKIDENT ('dbo.Tbl', RESEED, @maxID)
;

0

完全なDELETE行とIDENTITYカウントをリセットするには、これを使用します(SQL Server 2008 R2)

USE mydb

-- ##################################################################################################################
-- DANGEROUS!!!! USE WITH CARE
-- ##################################################################################################################

DECLARE
  db_cursor CURSOR FOR
    SELECT TABLE_NAME
      FROM INFORMATION_SCHEMA.TABLES
     WHERE TABLE_TYPE = 'BASE TABLE'
       AND TABLE_CATALOG = 'mydb'

DECLARE @tblname VARCHAR(50)
SET @tblname = ''

OPEN db_cursor
FETCH NEXT FROM db_cursor INTO @tblname

WHILE @@FETCH_STATUS = 0
BEGIN
  IF CHARINDEX('mycommonwordforalltablesIwanttodothisto', @tblname) > 0
    BEGIN
      EXEC('DELETE FROM ' + @tblname)
      DBCC CHECKIDENT (@tblname, RESEED, 0)
    END

  FETCH NEXT FROM db_cursor INTO @tblname
END

CLOSE db_cursor
DEALLOCATE db_cursor
GO

0

テーブル全体をクリーンアップしない限り、0への再シードはあまり実用的ではありません。

そうでなければ、Anthony Raymondの答えは完璧です。最初にID列の最大値を取得してから、最大値をシードします。


0

私は開発中に多数のテーブルに対してこれを実行するように努めてきましたが、これは魅力的に機能します。

DBCC CHECKIDENT('www.newsType', RESEED, 1);
DBCC CHECKIDENT('www.newsType', RESEED);

したがって、最初に強制的に1に設定し、次にテーブルに存在する行の最高のインデックスに設定します。idexの残りの部分はすばやく簡単です。


-2

ログスペースも使用しないため、すべてのレコードを削除するのではなく、可能な場合は常にTRUNCATEを使用することをお 勧めします。

削除が必要でシードをリセットする必要がある場合は、テーブルにデータが入力されておらず、使用したDBCC CHECKIDENT('tablenem',RESEED,0) 場合、msdnのドキュメントに記載されているように、最初のレコードはID = 0になることに注意して ください。

あなたのケースでは、インデックス再構築するだけであり、これは一般的なシナリオであるため、一連のIDを失う心配はありません。


3
一部のレコードのみを削除するのが私の考えのように思えます。
Drumbeg、2016年

6
これは単なる間違いです-常に切り捨てを使用するのは<i>常に</ i>良くなく、実際には、いくつかの非常に限定された特定のシナリオでのみ優れています。天が誰かがあなたのアドバイスに従うことを禁じ、それからロールバックする必要があります。
Thronk 2016

1
@ThronkなぜあなたはそれTRUNCATEROLLBACK期待通りに振る舞うのを防ぐだろうと暗示するのですか?ROLLBACKは引き続きロールバックします。DBがに設定されていてもBULK_LOGGED
ソロモンルツキー2017

2
TRUNCATEはDDL操作であり、ログファイルに記録されません。トランザクションの一部でない限り(質問またはこの回答では言及されていません)。誰かがいつも本当だと言うときはいつでも、それは彼らが間違っているのはかなり安全な賭けです。
Thronk

これは、シーケンスが以前に使用されたかどうかによってRESEEDの動作に違いがあることを示す唯一の回答です。複数の空のテーブル間で同じ値を再シードすると、いくつかのテーブルが以前に入力されていたため、各テーブルに挿入された最初のレコードの初期値が異なります。
サイモンコールマン

-4

最初:ID仕様Just: "いいえ" >>データベースの保存プロジェクトの実行

その後:Identity Specification Just: "YES" >>データベースの保存プロジェクトの実行

データベースID、PK 1から開始>>

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.