回答:
あなたの質問を理解する方法は、これまで手動値が入力されている列を持つ既存のテーブルがあり、(1)この列をIDENTITY
列にし、(2)IDENTITY
開始することを確認することです既存の行の最新の値から。
まず、いくつかのテストデータを使用して再生します。
CREATE TABLE dbo.ident_test (
id int NOT NULL,
xyz varchar(10) NOT NULL,
CONSTRAINT PK_ident_test PRIMARY KEY CLUSTERED (id)
);
INSERT INTO dbo.ident_test (id, xyz)
VALUES (1, 'test'),
(2, 'test'),
(5, 'test'),
(6, 'test'),
(10, 'test'),
(18, 'test'),
(19, 'test'),
(20, 'test');
目標は、テーブルの主キー列を作成することです。この列はid
、IDENTITY
挿入される次のレコードの21から始まる列です。この例では、列xyz
はテーブルの他のすべての列を表します。
何かを行う前に、この投稿の下部にある警告をお読みください。
まず、何かがうまくいかない場合に備えて:
BEGIN TRANSACTION;
次に、一時的な作業列を追加し、id_temp
その列を既存のid
列の値に設定します。
ALTER TABLE dbo.ident_test ADD id_temp int NULL;
UPDATE dbo.ident_test SET id_temp=id;
次に、既存のid
列をドロップする必要があります(既存の列に「追加」するだけではなくIDENTITY
、列をとして作成する必要がありますIDENTITY
)。列はそれに依存するため、主キーも移動する必要があります。
ALTER TABLE dbo.ident_test DROP CONSTRAINT PK_ident_test;
ALTER TABLE dbo.ident_test DROP COLUMN id;
...カラムを再度追加します。今回IDENTITY
は、主キーとともに、
ALTER TABLE dbo.ident_test ADD id int IDENTITY(1, 1) NOT NULL;
ALTER TABLE dbo.ident_test ADD CONSTRAINT PK_ident_test PRIMARY KEY CLUSTERED (id);
ここが面白いところです。IDENTITY_INSERT
テーブルで有効にできます。つまり、IDENTITY
新しい行を挿入するときに列の値を手動で定義できます(ただし、既存の行は更新しません)。
SET IDENTITY_INSERT dbo.ident_test ON;
そのセットでDELETE
は、テーブル内のすべての行が削除されますが、削除する行OUTPUT
はまったく同じテーブル内にありますが、id
(バックアップ列からの)列に特定の値があります。
DELETE FROM dbo.ident_test
OUTPUT deleted.id_temp AS id, deleted.xyz
INTO dbo.ident_test (id, xyz);
完了したら、IDENTITY_INSERT
再び電源を切ります。
SET IDENTITY_INSERT dbo.ident_test OFF;
追加した一時列を削除します。
ALTER TABLE dbo.ident_test DROP COLUMN id_temp;
そして最後に、IDENTITY
列を再シードします。そのため、次のレコードid
は、id
列の既存の最大番号の後に再開されます。
DECLARE @maxid int;
SELECT @maxid=MAX(id) FROM dbo.ident_test;
DBCC CHECKIDENT ("dbo.ident_test", RESEED, @maxid)
サンプルテーブルを確認すると、最大id
数は20です。
SELECT * FROM dbo.ident_test;
別の行を追加して、新しい行を確認しますIDENTITY
。
INSERT INTO dbo.ident_test (xyz) VALUES ('New row');
SELECT * FROM dbo.ident_test;
この例では、新しい行にはがありid=21
ます。最後に、満足であれば、トランザクションをコミットします。
COMMIT TRANSACTION;
重要
これは簡単な操作ではなく、注意すべきリスクがかなりあります。
これは専用のテスト環境で行います。バックアップします。:)
変更中にBEGIN/COMMIT TRANSACTION
他のプロセスがテーブルに干渉することを防ぎ、何か問題が発生した場合にすべてをロールバックできる可能性があるため、使用するのが好きです。ただし、トランザクションをコミットする前にテーブルにアクセスしようとする他のプロセスはすべて待機状態になります。これは、大きなテーブルがある場合や、実稼働環境にいる場合は、かなり悪い場合があります。
OUTPUT .. INTO
ターゲットテーブルに外部キー制約または頭の外では思い出せない他の多くの機能のいずれかがある場合、機能しません。代わりに、データを一時テーブルにオフロードしてから、元のテーブルに挿入することもできます。パーティションスイッチングを使用できる場合があります(パーティションを使用しない場合でも)。
これらのステートメントは、バッチまたはストアドプロシージャとしてではなく、1つずつ実行します。
id
ドロップして再作成する列に依存する可能性のある他のことを考えてみてください。インデックスを削除して再作成する必要があります(プライマリキーで行ったように)。事前に再作成する必要があるすべてのインデックスと制約をスクリプト化することを忘れないでください。
すべて無効INSERT
とDELETE
表のトリガー。
テーブルの再作成がオプションの場合:
テーブルの再作成がオプションである場合、すべてがはるかに簡単です:
id
列をとして空のテーブルを作成しますIDENTITY
。IDENTITY_INSERT ON
テーブルに設定、IDENTITY_INSERT OFF
、IDENTITY_INSERT ON
、入力、無効化するだけです。それは私がやりたかったことですが、MSSQLがそれをサポートしていることを知りませんでした。
UPDATE、DELETE、またはINSERTを使用してデータを移動するには、かなりの時間がかかり、データとログファイル/ディスクの両方でリソース(IO)を使用します。大きなテーブルで作業しているときに、トランザクションログが大量のレコードでいっぱいになるのを避けることができます。パーティションスイッチングを使用すると、メタデータのみが変更されます。
データの移動は含まれないため、これは非常に迅速に(ほぼ瞬時に)実行されます。
質問には、元のテーブルDDLは表示されません。この回答では、次のDDLが例として使用されます。
CREATE TABLE dbo.idT(
id int not null
, uid uniqueidentifier not null
, name varchar(50)
);
ALTER TABLE dbo.idT ADD CONSTRAINT PK_idT PRIMARY KEY CLUSTERED(id);
このクエリでは、0から15までの半ダースのダミーランダムIDが追加されます。
WITH ids(n) AS(
SELECT x1.n+x2.n*4
FROM (values(0), (3)) as x1(n)
CROSS JOIN (values(0), (2), (3)) as x2(n)
)
INSERT INTO idt(id, uid, name)
SELECT n, NEWID(), NEWID()
FROM ids
IdT
id uid name
0 65533096-5007-43EA-88AD-D6776B3B94FA 6A69D4F2-D682-4168-A92F-4CD2E2DBC21D
3 CE87F1ED-BE1A-4F2D-8D62-E1ECA822D35B AF0524D9-0DBB-41E1-883B-003CB4E4F012
8 34A1DBFD-4F92-4F34-9F04-4CDC824AB15A 02B4BDA4-D515-4262-9031-0BE496AC24CE
11 51606C95-9DE8-4C30-B23B-F915EEA41156 93258103-9C22-4F9C-85CF-712ED0FB3CE6
12 CEC80431-0513-4751-A250-0EB3390DACAB 2DA6B8AF-3EBC-42B3-A76C-028716E24661
15 5037EA83-286F-4EBC-AD7C-E237B570C1FF 095E51E9-8C38-4104-858F-D14AA810A550
IDENTITY(0, 1)
唯一の問題idT
は、IDENTITY(0, 1)
id のプロパティがないことです。同様の構造を持つ新しいテーブルIDENTITY(0, 1)
が作成されます:
CREATE TABLE dbo.idT_Switch(
id int identity(0, 1) not null
, uid uniqueidentifier not null
, name varchar(50)
);
ALTER TABLE dbo.idT_Switch ADD CONSTRAINT PK_idT_Switch PRIMARY KEY CLUSTERED(id);
以外はIDENTITY(0, 1)
、idT_Switch
と同じですidT
。
idT
この手法を使用するには、外部キーを削除する必要があります。
idT
そしてidT_Switch
テーブルは互換性のある構造を有しています。代わりに使用するのではDELETE
、UPDATE
とINSERT
の行を移動するためのステートメントをidT
するidT_Switch
か、上のidT
自体、ALTER TABLE ... SWITCH
使用することができます。
ALTER TABLE dbo.idT
SWITCH TO dbo.idT_Switch;
PK_idT
(テーブル全体)の単一の「パーティション」が移動されますPK_idT_Switch
(逆も同様です)。idT
現在、0行とidT_Switch
6行が含まれています。
ソースと宛先の互換性要件の完全なリストは、次の場所にあります。
SWITCH
明示的なパーティショニングがないため、この使用にはEnterprise Editionは必要ありません。パーティション化されていないテーブルは、SQL Server 2005以降の単一パーティションを持つテーブルと見なされます。
idT
idT
現在は空で役に立たず、ドロップできます:
DROP TABLE idT;
idT_Switch
名前を変更することができ、古いidT
テーブルを置き換えます:
EXECUTE sys.sp_rename
@objname = N'dbo.idT_Switch',
@newname = N'idT', -- note lack of schema prefix
@objtype = 'OBJECT';
外部キーを新しいidT
テーブルに再度追加できます。idT
切り替えに対応したテーブルを作成するために以前に削除されたものもすべてやり直す必要があります。
SELECT IDENT_CURRENT( 'dbo.idT');
このコマンドは0を返します。テーブルidTには、MAX(id)= 15の6行が含まれます。DBCCCHECKIDENT(table_name)を使用できます。
DBCC CHECKIDENT ('dbo.idT');
15は0より大きいため、MAX(id)を探すことなく自動的に再シードされます。
テーブルの現在のID値がID列に格納されている最大ID値より小さい場合、ID列の最大値を使用してリセットされます。次の「例外」セクションを参照してください。
IDENT_CURRENTは15を返します。
簡単なINSERT
声明:
INSERT INTO idT(uid, name) SELECT NEWID(), NEWID();
この行を追加します。
id uid name
16 B395D692-5D7B-4DFA-9971-A1497B8357A1 FF210D9E-4027-479C-B5D8-057E77FAF378
id
カラムは、現在のアイデンティティを使用しており、新たに挿入された値は、実際に16(+ 1 15)です。
関連する質問と回答があり、SWITCH
テクニックの背景がここにあります:
新しいID値で開始する場合は、IDを再シードする必要があります。のドキュメントをご覧くださいCHECKIDENT
DBCC CHECKIDENT (yourtable, reseed, starting point)
IDENTITY
ですか?