既存のPKに自動インクリメントを追加する


13

別のDBに既に存在するDBにテーブルを作成しました。最初は古いDBデータが入力されていました。テーブルのPKは、それらのレコードに既に存在する値を受信する必要があったため、自動インクリメントすることはできませんでした。

ここで、PKを自動インクリメントとして使用する新しいテーブルが必要です。しかし、PKが既に存在し、データを取得した後、どうすればそれができますか?


3
「自動インクリメント」と言うとき、正確には何を指しているのですか?SQL Serverでは、列にそのようなプロパティはありません。という意味IDENTITYですか?
マックスヴァーノン

はい、それがMSSQLでの呼び出し方です。一般的なデータベースでは、自動インクリメントPKです。

回答:


13

あなたの質問を理解する方法は、これまで手動値が入力されている列を持つ既存のテーブルがあり、(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');

目標は、テーブルの主キー列を作成することです。この列はidIDENTITY挿入される次のレコードの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ドロップして再作成する列に依存する可能性のある他のことを考えてみてください。インデックスを削除して再作成する必要があります(プライマリキーで行ったように)。事前に再作成する必要があるすべてのインデックスと制約をスクリプト化することを忘れないでください。

  • すべて無効INSERTDELETE表のトリガー。

テーブルの再作成がオプションの場合:

テーブルの再作成がオプションである場合、すべてがはるかに簡単です:

  • id列をとして空のテーブルを作成しますIDENTITY
  • IDENTITY_INSERT ONテーブルに設定、
  • テーブルにデータを入力し、
  • 設定しIDENTITY_INSERT OFF
  • アイデンティティを再シードします。

すばらしい回答、どうもありがとう!実際、私の場合は、設定IDENTITY_INSERT ON、入力、無効化するだけです。それは私がやりたかったことですが、MSSQLがそれをサポートしていることを知りませんでした。

5

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テーブルは互換性のある構造を有しています。代わりに使用するのではDELETEUPDATEINSERTの行を移動するためのステートメントをidTするidT_Switchか、上のidT自体、ALTER TABLE ... SWITCH使用することができます。

ALTER TABLE dbo.idT
SWITCH TO dbo.idT_Switch;

PK_idT(テーブル全体)の単一の「パーティション」が移動されますPK_idT_Switch(逆も同様です)。idT現在、0行とidT_Switch6行が含まれています。

ソースと宛先の互換性要件の完全なリストは、次の場所にあります。

パーティションスイッチングを使用したデータの効率的な転送

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テクニックの背景がここにあります:

列のIdentityプロパティの削除がサポートされないのはなぜですか



0

IDENTITY_INSERTの有効化と無効化

テーブルがTABLE_Aの場合

  1. ID列を持つTABLE_Aに似たCREATE TABLE TABLE_B
  2. SET IDENTITY_INSERT TABLE_B ON
  3. TABLE_AからTABLE_Bへの挿入
  4. SET IDENTITY_INSERT TABLE_B OFF
  5. DROP TABLE TABLE_Aおよび名前変更テーブルB Exec sp_rename 'TABLE_B'、 'TABLE_A'
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.