テーブルの列からIDを削除する


127

5 GBのテーブル(約5億行)があり、いずれかの列のIDプロパティを削除したいのですが、SSMSを使用してこれを実行しようとすると、タイムアウトします。

これはT-SQLを使用して実行できますか?


1
ここにテーブルのスキーマを投稿できますか?
Al W

SQL Serverが単純なALTER TABLE ...ステートメントを介して列からIDプロパティを削除することをサポートしていないという優れた理由があると思いますが、それでも、それが今のところ悲しくなっています。
Jon Schneider

回答:


143

IDENTITY一度設定した仕様は削除できません。

列全体を削除するには:

ALTER TABLE yourTable
DROP COLUMN yourCOlumn;

ここでのALTER TABLEに関する情報

データを保持する必要があるが、IDENTITY列を削除する場合は、次のことを行う必要があります。

  • 新しい列を作成する
  • 既存のIDENTITY列から新しい列にデータを転送する
  • 既存のIDENTITY列をドロップします。
  • 新しい列の名前を元の列の名前に変更します

3
Identity仕様を削除できます。実際、5億行ではありませんが、昨日SSMSを使用してそれを行わなければなりませんでした。
Simon、

33
@simon変更をスクリプト化すると、SSMSが実際にIDプロパティなしでテーブルのコピーを作成していることがわかります。
Code Magician

3
新しい列の名前を元の列の名前に変更して追加したいだけです。また、このidentity列がforeign key別のテーブルのの一部として使用されている場合は、最初に制約を削除してから、attribute/property@ AdamWengerがIDの削除について述べたようにアクションを実行する必要があります。詳細については、このリンクを調べることもできます。属性のみの削除について:blog.sqlauthority.com/2009/05/03/… ..頑張ってください!
Nymym

1
反対票を投じた方へ-私の答えについてあなたが気に入らなかったことを聞いていただければ幸いです。
Adam Wenger

1
以下のMark Sowulの回答をご覧ください。列間でデータを移動する必要がなくなります。マークの答えを使用すると、メタデータをシャッフルするだけです。数千万から数億の行があるテーブルで作業しているのでなければ、大したことはありません。プラスマークの答えは、テーブルスキーマの列の再配置を防ぎます。私はそれを試してみましたが、それは魅力のように機能しました。非常に賢い。
Andrew Steitz、2016年

100

新しい列を追加したり、列を並べ替えたりせずに、また、テーブルでデータが変更されていないためにダウンタイムがほとんど発生せずにこれを実行する場合は、パーティション分割機能を使用して魔法をかけましょう(ただし、パーティションは使用されないため、使用しません) Enterprise Editionが必要です):

  1. このテーブルを指すすべての外部キーを削除します
  2. 作成するテーブルのスクリプトを作成します。「MyTable2」、「MyIndex2」など、すべての名前を変更します。IDENTITYの指定を削除します。
  3. これで、2つの「同一」のようなテーブルができます。1つは完全で、もう1つはIDENTITYのない空です。
  4. 走る ALTER TABLE [Original] SWITCH TO [Original2]
  5. これで、元のテーブルは空になり、新しいテーブルにデータが含まれます。2つのテーブルのメタデータを切り替えました(インスタント)。
  6. 元の(現在は空のテーブル)をexec sys.sp_rename削除して、さまざまなスキーマオブジェクトの名前を元の名前に戻し、外部キーを再作成できます。

たとえば、次の場合:

CREATE TABLE Original
(
  Id INT IDENTITY PRIMARY KEY
, Value NVARCHAR(300)
);
CREATE NONCLUSTERED INDEX IX_Original_Value ON Original (Value);

INSERT INTO Original
SELECT 'abcd'
UNION ALL 
SELECT 'defg';

次のことができます。

--create new table with no IDENTITY
CREATE TABLE Original2
(
  Id INT PRIMARY KEY
, Value NVARCHAR(300)
);
CREATE NONCLUSTERED INDEX IX_Original_Value2 ON Original2 (Value);

--data before switch
SELECT 'Original', *
FROM Original
UNION ALL
SELECT 'Original2', *
FROM Original2;

ALTER TABLE Original SWITCH TO Original2;

--data after switch
SELECT 'Original', *
FROM Original
UNION ALL
SELECT 'Original2', *
FROM Original2;

--clean up 
IF NOT EXISTS (SELECT * FROM Original) DROP TABLE Original;
EXEC sys.sp_rename 'Original2.IX_Original_Value2', 'IX_Original_Value', 'INDEX';
EXEC sys.sp_rename 'Original2', 'Original', 'OBJECT';


UPDATE Original
SET Id = Id + 1;

SELECT *
FROM Original;

これを実際に試す時間はありませんが、次にIDをドロップする必要があるときに知ることは良いことです。
CoderDennis

11
これは受け入れられる答えになるはずです。これは、大量のデータ移行を行わずにID列を削除する唯一の実際の方法です。
Vaccano 2016年

うわー!これはとても賢いです。
Andrew Steitz、2016年

この答えは驚異的です!ありがとう!
Jon

5
テーブルからスクリプトにSQL Management Studioを使用している場合は、[ツール]をオンにしてください> [オプション]> [SQL Serverのオブジェクトエクスプローラ>スクリプティング>テーブルとビューのオプション>(デフォルトはfalse)スクリプト・インデックス
user423430

61

これは、外部キーと主キーの制約で乱雑になるので、途中で役立つスクリプトを次に示します。

まず、一時的な名前で重複する列を作成します。

alter table yourTable add tempId int NOT NULL default -1;
update yourTable set tempId = id;

次に、主キー制約の名前を取得します。

SELECT * FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_NAME = 'yourTable';

次に、列の主キー制約を削除してみます。

ALTER TABLE yourTable DROP CONSTRAINT PK_yourTable_id;

外部キーがある場合は失敗します。その場合は外部キー制約を削除してください。 後で制約を追加できるように、これを実行するテーブルのトラックを保持してください!!!

SELECT * FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_NAME = 'otherTable';
alter table otherTable drop constraint fk_otherTable_yourTable;
commit;
..

すべての外部キー制約が削除されたら、PK制約を削除し、その列を削除して、一時列の名前を変更し、PK制約をその列に追加できます。

ALTER TABLE yourTable DROP CONSTRAINT PK_yourTable_id;
alter table yourTable drop column id;
EXEC sp_rename 'yourTable.tempId', 'id', 'COLUMN';
ALTER TABLE yourTable ADD CONSTRAINT PK_yourTable_id PRIMARY KEY (id) 
commit;

最後に、FK制約を追加して戻します。

alter table otherTable add constraint fk_otherTable_yourTable foreign key (yourTable_id) references yourTable(id);
..

エルフィン!


SQLサーバーのバージョンに注意してください。私はAzure SQLサーバーで試しましたが、ここでのすべての操作がAzureバージョンのSQLでサポートされているわけではありません。
dasons 2015年

答えてくれてありがとう!とても助かりました!列を参照するインデックスの検証を追加するだけです。例: Select t.name 'Table', i.name 'Index', c.name 'Column', i.is_primary_key, i.is_unique From sys.tables t Inner Join sys.indexes i On i.object_id = t.object_id Inner Join sys.index_columns ic ON ic.object_id = i.object_id And i.index_id = ic.index_id Inner Join sys.columns c ON ic.object_id = c.object_id and ic.column_id = c.column_id Where t.name = 'tableName' Order By t.name, i.name, i.index_id, ic.index_column_id
Alexandre Junges

良いトリック、私を助けてくれましたが、1つ目のポイントは、ここで触れておきたいことです。最初のステップで述べたように新しい重複列を追加すると、最後に追加されますが、通常、主キー列、つまりIdそのテーブルの最初の列。それでそれに対する回避策はありますか?
Ashish Shukla

19

私はこれと同じ問題を抱えていました。GUIを使用する代わりにSSMSで4つのステートメントを使用すると、非常に高速でした。

  • 新しい列を作成する

    alter table users add newusernum int;

  • 値をコピー

    update users set newusernum=usernum;

  • 古い列をドロップします

    alter table users drop column usernum;

  • 新しい列の名前を古い列の名前に変更します

    EXEC sp_RENAME 'users.newusernum' , 'usernum', 'COLUMN';


古い列に制約がある場合、これは簡単ではありません。
Suncat2000

13

次のスクリプトは、「Id」という名前の列のIdentityフィールドを削除します

それが役に立てば幸い。

BEGIN TRAN
BEGIN TRY
    EXEC sp_rename '[SomeTable].[Id]', 'OldId';

    ALTER TABLE [SomeTable] ADD Id int NULL

    EXEC ('UPDATE [SomeTable] SET Id = OldId')

    ALTER TABLE [SomeTable] NOCHECK CONSTRAINT ALL

    ALTER TABLE [SomeTable] DROP CONSTRAINT [PK_constraintName];
    ALTER TABLE [SomeTable] DROP COLUMN OldId
    ALTER TABLE [SomeTable] ALTER COLUMN [Id] INTEGER NOT NULL
    ALTER TABLE [SomeTable] ADD CONSTRAINT PK_JobInfo PRIMARY KEY (Id)

    ALTER TABLE [SomeTable] CHECK CONSTRAINT ALL

    COMMIT TRAN
END TRY
BEGIN CATCH
    ROLLBACK TRAN   
    SELECT ERROR_MESSAGE ()
END CATCH

4

ID列名がわからない場合は、以下のコードで問題なく動作します

のような新しい一時テーブルにデータをコピーする必要がありますInvoice_DELETED。次回使用するとき:

insert into Invoice_DELETED select * from Invoice where ...


SELECT t1.*
INTO Invoice_DELETED
FROM Invoice t1
LEFT JOIN Invoice ON 1 = 0
--WHERE t1.InvoiceID = @InvoiceID

詳細については、https//dba.stackexchange.com/a/138345/101038を参照して ください。


1
LOVE YAAAAAAAAAA。まさに私が探していたもの、「SELECT * INTO」から「IDENTITY」を生成しない簡単な方法(必要なバックアップ用に新しいテーブルtempを作成する)
KurzedMetal

それは小さくて美しいです;)
ゾルファガリ

3
ALTER TABLE tablename add newcolumn int
update tablename set newcolumn=existingcolumnname
ALTER TABLE tablename DROP COLUMN existingcolumnname;
EXEC sp_RENAME 'tablename.oldcolumn' , 'newcolumnname', 'COLUMN'

ただし、上記のコードは、主外部キー関係がない場合にのみ機能します


1

同じ問題を抱えている人のためだけに。一度だけ挿入したい場合は、このようなことを行うことができます。

2列のテーブルがあるとしましょう

ID Identity (1,1) | Name Varchar

ID = 4の行を挿入したいので、それを3に再シードして、次の行を4にします。

DBCC CHECKIDENT([YourTable], RESEED, 3)

挿入する

INSERT  INTO [YourTable]
        ( Name )
VALUES  ( 'Client' )

シードを最高のIDに戻します。15だとしましょう

DBCC CHECKIDENT([YourTable], RESEED, 15)

できた!


1

同じ要件があり、この方法を試すこともできますが、個人的にはお勧めします。手動でテーブルを設計してスクリプトを生成してください。以下で行ったのは、古いテーブルの名前を変更することと、バックアップの制約です。

/* To prevent any potential data loss issues, you should review this script in detail before running it outside the context of the database designer.*/
BEGIN TRANSACTION

SET QUOTED_IDENTIFIER ON
SET ARITHABORT ON
SET NUMERIC_ROUNDABORT OFF
SET CONCAT_NULL_YIELDS_NULL ON
SET ANSI_NULLS ON
SET ANSI_PADDING ON
SET ANSI_WARNINGS ON
COMMIT
BEGIN TRANSACTION
GO
ALTER TABLE dbo.SI_Provider_Profile
    DROP CONSTRAINT DF_SI_Provider_Profile_SIdtDateTimeStamp
GO
ALTER TABLE dbo.SI_Provider_Profile
    DROP CONSTRAINT DF_SI_Provider_Profile_SIbHotelPreLoaded
GO
CREATE TABLE dbo.Tmp_SI_Provider_Profile
    (
    SI_lProvider_Profile_ID int NOT NULL,
    SI_lSerko_Integrator_Token_ID int NOT NULL,
    SI_sSerko_Integrator_Provider varchar(50) NOT NULL,
    SI_sSerko_Integrator_Profile varchar(50) NOT NULL,
    SI_dtDate_Time_Stamp datetime NOT NULL,
    SI_lProvider_ID int NULL,
    SI_sDisplay_Name varchar(10) NULL,
    SI_lPurchased_From int NULL,
    SI_sProvider_UniqueID varchar(255) NULL,
    SI_bHotel_Pre_Loaded bit NOT NULL,
    SI_sSiteName varchar(255) NULL
    )  ON [PRIMARY]
GO
ALTER TABLE dbo.Tmp_SI_Provider_Profile SET (LOCK_ESCALATION = TABLE)
GO
ALTER TABLE dbo.Tmp_SI_Provider_Profile ADD CONSTRAINT
    DF_SI_Provider_Profile_SIdtDateTimeStamp DEFAULT (getdate()) FOR SI_dtDate_Time_Stamp
GO
ALTER TABLE dbo.Tmp_SI_Provider_Profile ADD CONSTRAINT
    DF_SI_Provider_Profile_SIbHotelPreLoaded DEFAULT ((0)) FOR SI_bHotel_Pre_Loaded
GO
IF EXISTS(SELECT * FROM dbo.SI_Provider_Profile)
        EXEC('INSERT INTO dbo.Tmp_SI_Provider_Profile (SI_lProvider_Profile_ID, SI_lSerko_Integrator_Token_ID, SI_sSerko_Integrator_Provider, SI_sSerko_Integrator_Profile, SI_dtDate_Time_Stamp, SI_lProvider_ID, SI_sDisplay_Name, SI_lPurchased_From, SI_sProvider_UniqueID, SI_bHotel_Pre_Loaded, SI_sSiteName)
        SELECT SI_lProvider_Profile_ID, SI_lSerko_Integrator_Token_ID, SI_sSerko_Integrator_Provider, SI_sSerko_Integrator_Profile, SI_dtDate_Time_Stamp, SI_lProvider_ID, SI_sDisplay_Name, SI_lPurchased_From, SI_sProvider_UniqueID, SI_bHotel_Pre_Loaded, SI_sSiteName FROM dbo.SI_Provider_Profile WITH (HOLDLOCK TABLOCKX)')
GO

-- Rename the primary key constraint or unique key In SQL Server constraints such as primary keys or foreign keys are objects in their own right, even though they are dependent upon the "containing" table.
EXEC sp_rename 'dbo.SI_Provider_Profile.PK_SI_Provider_Profile', 'PK_SI_Provider_Profile_Old';
GO
-- backup old table in case of 
EXECUTE sp_rename N'dbo.SI_Provider_Profile', N'SI_Provider_Profile_Old', 'OBJECT'
GO

EXECUTE sp_rename N'dbo.Tmp_SI_Provider_Profile', N'SI_Provider_Profile', 'OBJECT'
GO

ALTER TABLE dbo.SI_Provider_Profile ADD CONSTRAINT
    PK_SI_Provider_Profile PRIMARY KEY NONCLUSTERED 
    (
    SI_lProvider_Profile_ID
    ) WITH( PAD_INDEX = OFF, FILLFACTOR = 90, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]

GO
COMMIT TRANSACTION

1

SQL Serverでは、次のようにID挿入のオンとオフを切り替えることができます。

SET IDENTITY_INSERT table_name ON

-ここでクエリを実行します

SET IDENTITY_INSERT table_name OFF

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