Hapus Identitas dari kolom di tabel

127

Kami memiliki tabel 5GB (hampir 500 juta baris) dan kami ingin menghapus properti identitas di salah satu kolom, tetapi ketika kami mencoba melakukan ini melalui SSMS - waktu habis.

Bisakah ini dilakukan melalui T-SQL?

Conrad Jagger
sumber
1
Bisakah Anda memposting skema tabel di sini?
Al W
Saya yakin ada alasan bagus mengapa SQL Server tidak mendukung penghapusan properti identitas dari kolom melalui pernyataan ALTER TABLE ... sederhana, tetapi itu membuat saya sedih saat ini bahwa itu masalahnya.
Jon Schneider

Jawaban:

143

Anda tidak dapat menghapus IDENTITYspesifikasi setelah disetel.

Untuk menghapus seluruh kolom:

ALTER TABLE yourTable
DROP COLUMN yourCOlumn;

Informasi tentang ALTER TABLE di sini

Jika Anda perlu menyimpan data, tetapi menghapus IDENTITYkolomnya, Anda perlu:

  • Buat kolom baru
  • Transfer data dari IDENTITYkolom yang ada ke kolom baru
  • Jatuhkan IDENTITYkolom yang ada .
  • Ubah nama kolom baru menjadi nama kolom asli
Adam Wenger
sumber
3
Anda dapat menghapus spesifikasi Identity. Sebenarnya saya harus melakukannya kemarin menggunakan SSMS meskipun tidak pada 500 juta baris.
Simon
34
@simon jika Anda membuat skrip perubahan Anda, Anda akan melihat SSMS sebenarnya membuat salinan tabel tanpa properti identitas.
Code Magician
3
Saya hanya ingin menambahkan untuk mengganti nama kolom baru menjadi nama kolom asli. Selain itu, jika identitykolom ini digunakan sebagai bagian dari a foreign keydi tabel lain, Anda harus menghapus pembatas terlebih dahulu, lalu mengambil tindakan seperti yang disebutkan @AdamWenger tentang penghapusan identitas attribute/property.. Anda juga dapat melihat link ini untuk lebih jelasnya tentang menghapus atribut saja: blog.sqlauthority.com/2009/05/03/… ..Semoga berhasil!
Nonim
1
Kepada orang yang tidak memilih - saya akan sangat senang mendengar apa yang tidak Anda sukai tentang jawaban saya.
Adam Wenger
1
Simak jawaban Mark Sowul di bawah ini. Menghilangkan kebutuhan untuk memindahkan data dari kolom ke kolom. Menggunakan jawaban Mark, Anda hanya mengacak metadata. Bukan masalah besar kecuali Anda mengerjakan tabel yang memiliki puluhan atau ratusan juta baris. Jawaban Tanda Tambah mencegah relokasi kolom dalam skema tabel. Saya baru saja mencobanya dan hasilnya bagus. Sangat pintar.
Andrew Steitz
100

Jika Anda ingin melakukan ini tanpa menambah dan mengisi kolom baru , tanpa menyusun ulang kolom , dan hampir tidak ada waktu henti karena tidak ada data yang berubah pada tabel, mari lakukan keajaiban dengan fungsionalitas partisi (tetapi karena tidak ada partisi yang digunakan, Anda tidak akan melakukannya) t perlu edisi Enterprise):

  1. Hapus semua kunci asing yang mengarah ke tabel ini
  2. Script tabel yang akan dibuat; ganti nama semuanya misalnya 'MyTable2', 'MyIndex2', dll. Hapus spesifikasi IDENTITAS.
  3. Anda sekarang harus memiliki dua tabel -ish "identik", satu penuh, yang lain kosong tanpa IDENTITAS.
  4. Lari ALTER TABLE [Original] SWITCH TO [Original2]
  5. Sekarang tabel asli Anda akan kosong dan tabel baru akan memiliki datanya. Anda telah mengganti metadata untuk dua tabel (instan).
  6. Lepaskan yang asli (tabel sekarang kosong), exec sys.sp_renameuntuk mengganti nama berbagai objek skema kembali ke nama aslinya, dan kemudian Anda dapat membuat ulang kunci asing Anda.

Misalnya, diberikan:

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';

Anda dapat melakukan hal berikut:

--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;
Mark Sowul
sumber
Saya tidak punya waktu untuk benar-benar mencoba ini, tetapi ada baiknya untuk mengetahui saat berikutnya saya benar-benar perlu melepaskan identitas.
CoderDennis
12
Ini harus menjadi jawaban yang diterima. Ini adalah satu-satunya cara nyata untuk menghapus kolom identitas tanpa migrasi data besar-besaran.
Vaccano
Wow! Ini sangat pintar.
Andrew Steitz
Jawaban ini sangat fenomenal! Terima kasih!
Jon
5
Jika menggunakan SQL Management Studio untuk membuat skrip tabel, pastikan untuk mengaktifkan Alat> Opsi> SQL Server Object Explorer> Scripting> Tabel dan opsi tampilan> Indeks skrip (False secara default)
user423430
61

Ini menjadi berantakan dengan kendala kunci asing dan kunci utama, jadi inilah beberapa skrip untuk membantu Anda dalam perjalanan:

Pertama, buat kolom duplikat dengan nama sementara:

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

Selanjutnya, dapatkan nama batasan kunci utama Anda:

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

Sekarang coba lepaskan kendala kunci utama untuk kolom Anda:

ALTER TABLE yourTable DROP CONSTRAINT PK_yourTable_id;

Jika Anda memiliki kunci asing, itu akan gagal, jadi jika demikian jatuhkan batasan kunci asing. TERUS LACAK DARI TABEL YANG ANDA JALANKAN INI SEHINGGA ANDA DAPAT MENAMBAHKAN BATAS KEMBALI DI NANTI !!!

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

Setelah semua batasan kunci asing Anda dihapus, Anda akan dapat menghapus batasan PK, menghapus kolom itu, mengganti nama kolom temp Anda, dan menambahkan batasan PK ke kolom itu:

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;

Terakhir, tambahkan kembali batasan FK:

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

El Fin!

Briguy37
sumber
perhatikan versi server sql Anda, saya mencoba di server sql azure, tidak semua operasi di sini didukung dalam versi azure sql.
alasan
Terima kasih atas jawabannya! Banyak membantu saya! Anda hanya perlu menambahkan verifikasi untuk indeks yang mereferensikan kolom. Sesuatu seperti: 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
trik yang bagus, banyak membantu saya, Tapi satu hal, yang ingin saya sebutkan di sini adalah, Ketika kita menambahkan kolom duplikat baru seperti yang disebutkan di langkah pertama, itu ditambahkan di akhir tetapi umumnya kita ingin kolom kunci utama kita yaitu Id menjadi kolom 1 di tabel itu. Jadi, apakah ada solusi untuk itu?
Ashish Shukla
19

Saya baru saja mengalami masalah yang sama. 4 pernyataan di SSMS daripada menggunakan GUI dan itu sangat cepat.

  • Buat kolom baru

    alter table users add newusernum int;

  • Salin nilai

    update users set newusernum=usernum;

  • Jatuhkan kolom lama

    alter table users drop column usernum;

  • Ubah nama kolom baru menjadi nama kolom lama

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

kebebasan_
sumber
Tidak sesederhana ini jika Anda memiliki batasan pada kolom lama.
Suncat2000
13

Skrip berikut menghapus bidang Identitas untuk kolom bernama 'Id'

Semoga membantu.

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
bluedot
sumber
4

Kode di bawah berfungsi dengan baik, ketika kita tidak tahu nama kolom identitas .

Perlu menyalin data ke tabel temp baru seperti Invoice_DELETED. dan lain kali kami menggunakan:

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

Untuk penjelasan lebih lanjut lihat: https://dba.stackexchange.com/a/138345/101038

Zolfaghari
sumber
1
CINTA YAAAAAAAAAA. Persis apa yang saya cari, cara mudah untuk menghindari menghasilkan "IDENTITAS" dari "SELECT * INTO" (untuk membuat suhu tabel baru untuk backup yang membutuhkan)
KurzedMetal
itu kecil dan indah;)
Zolfaghari
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'

Namun kode di atas hanya berfungsi jika tidak ada hubungan kunci asing-primer

Jekin Kalariya
sumber
1

Hanya untuk seseorang yang memiliki masalah yang sama dengan saya. Jika Anda hanya ingin membuat beberapa penyisipan sekali saja, Anda dapat melakukan sesuatu seperti ini.

Misalkan Anda memiliki tabel dengan dua kolom

ID Identity (1,1) | Name Varchar

dan ingin menyisipkan baris dengan ID = 4. Jadi Anda Ulangi ke 3 jadi yang berikutnya adalah 4

DBCC CHECKIDENT([YourTable], RESEED, 3)

Buat Sisipan

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

Dan dapatkan seed Anda kembali ke ID tertinggi, misalkan adalah 15

DBCC CHECKIDENT([YourTable], RESEED, 15)

Selesai!

Faruk Feres
sumber
1

Saya memiliki persyaratan yang sama, dan Anda dapat mencoba cara ini, yang secara pribadi saya rekomendasikan, desain tabel Anda secara manual dan buat skripnya, dan apa yang saya lakukan di bawah ini adalah mengganti nama tabel lama dan juga batasannya untuk cadangan.

/* 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
Cena Jang
sumber
1

Di SQL Server, Anda dapat mengaktifkan dan menonaktifkan penyisipan identitas seperti ini:

SET IDENTITY_INSERT table_name ON

- jalankan pertanyaan Anda di sini

NONAKTIFKAN nama_tabel IDENTITY_INSERT

pengguna2624918
sumber