ลบ Identity ออกจากคอลัมน์ในตาราง


127

เรามีตาราง 5GB (เกือบ 500 ล้านแถว) และเราต้องการลบคุณสมบัติประจำตัวในคอลัมน์ใดคอลัมน์หนึ่ง แต่เมื่อเราพยายามทำสิ่งนี้ผ่าน SSMS มันจะหมดเวลา

สามารถทำได้ผ่าน T-SQL หรือไม่?


1
คุณสามารถโพสต์สคีมาของตารางที่นี่ได้หรือไม่
Al W

ฉันแน่ใจว่ามีเหตุผลที่ดีเยี่ยมที่ SQL Server ไม่สนับสนุนการลบคุณสมบัติประจำตัวออกจากคอลัมน์ผ่านคำสั่ง ALTER TABLE ... แต่ก็ยังทำให้ฉันเสียใจในตอนนี้ที่เป็นเช่นนั้น
Jon Schneider

คำตอบ:


143

คุณไม่สามารถลบIDENTITYข้อกำหนดได้เมื่อตั้งค่าแล้ว

ในการลบทั้งคอลัมน์:

ALTER TABLE yourTable
DROP COLUMN yourCOlumn;

ข้อมูลเกี่ยวกับALTER TABLEที่นี่

หากคุณต้องการเก็บข้อมูล แต่ลบIDENTITYคอลัมน์ออกคุณจะต้อง:

  • สร้างคอลัมน์ใหม่
  • ถ่ายโอนข้อมูลจากIDENTITYคอลัมน์ที่มีอยู่ไปยังคอลัมน์ใหม่
  • วางไฟล์ IDENTITYคอลัมน์
  • เปลี่ยนชื่อคอลัมน์ใหม่เป็นชื่อคอลัมน์เดิม

3
คุณสามารถลบข้อมูลจำเพาะประจำตัวได้ อันที่จริงฉันต้องทำเมื่อวานนี้โดยใช้ SSMS แม้ว่าจะไม่ใช่ 500 ล้านแถว
Simon

34
@simon หากคุณเขียนสคริปต์การเปลี่ยนแปลงของคุณคุณจะเห็นว่า SSMS กำลังสร้างสำเนาของตารางโดยไม่มีคุณสมบัติข้อมูลประจำตัว
Code Magician

3
ฉันแค่ต้องการเพิ่มเพื่อเปลี่ยนชื่อคอลัมน์ใหม่เป็นชื่อคอลัมน์เดิม นอกจากนี้หากใช้identityคอลัมน์นี้เป็นส่วนหนึ่งของforeign keyตารางอื่นคุณจะต้องยกเลิกข้อ จำกัด ก่อนจากนั้นจึงดำเนินการตามที่ @AdamWenger กล่าวถึงการลบข้อมูลประจำตัวattribute/property.. คุณสามารถดูรายละเอียดเพิ่มเติมได้ที่ลิงค์นี้ เกี่ยวกับการลบแอตทริบิวต์เท่านั้น: blog.sqlauthority.com/2009/05/03/… .. ขอให้โชคดี!
ระบุ

1
สำหรับผู้ที่ลงคะแนน - ฉันยินดีที่ได้ยินสิ่งที่คุณไม่ชอบเกี่ยวกับคำตอบของฉัน
Adam Wenger

1
ดูคำตอบของ Mark Sowul ด้านล่าง ยอมรับความจำเป็นในการย้ายข้อมูลจากคอลัมน์หนึ่งไปอีกคอลัมน์ การใช้คำตอบของ Mark คุณกำลังสับเปลี่ยนข้อมูลเมตาเท่านั้น ไม่มีเรื่องใหญ่เว้นแต่คุณจะทำงานบนโต๊ะที่มีแถวหลายสิบหรือหลายร้อยล้านแถว คำตอบของ Plus Mark ป้องกันการย้ายตำแหน่งของคอลัมน์ในสคีมาของตาราง ฉันเพิ่งลองใช้และได้ผลเหมือนมีเสน่ห์ ฉลาดมาก.
Andrew Steitz

101

ถ้าคุณต้องการที่จะทำเช่นนี้โดยไม่ต้องเพิ่มและเติมคอลัมน์ใหม่ , โดยไม่ต้องเรียงลำดับคอลัมน์และเกือบไม่มีการหยุดทำงานเพราะไม่มีข้อมูลที่มีการเปลี่ยนแปลงบนโต๊ะให้ทำมายากลบางคนที่มีการแบ่งการทำงาน ( แต่เนื่องจากพาร์ทิชันไม่ถูกนำมาใช้คุณ don' ไม่จำเป็นต้องใช้รุ่น Enterprise):

  1. ลบคีย์แปลกปลอมทั้งหมดที่ชี้ไปที่ตารางนี้
  2. สคริปต์ตารางที่จะสร้าง เปลี่ยนชื่อทุกอย่างเช่น 'MyTable2', 'MyIndex2' ฯลฯ ลบข้อกำหนด IDENTITY
  3. ตอนนี้คุณควรมีตารางที่ "เหมือนกัน" สองตารางหนึ่งตารางเต็มและอีกตารางว่างเปล่าโดยไม่มี IDENTITY
  4. วิ่ง ALTER TABLE [Original] SWITCH TO [Original2]
  5. ตอนนี้ตารางเดิมของคุณจะว่างเปล่าและตารางใหม่จะมีข้อมูล คุณได้เปลี่ยนข้อมูลเมตาสำหรับสองตาราง (ทันที)
  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;

ฉันไม่มีเวลาลองทำแบบนี้ แต่มันเป็นเรื่องดีที่จะรู้ว่าในครั้งต่อไปฉันต้องละทิ้งตัวตนจริงๆ
CoderDennis

12
นี่ควรเป็นคำตอบที่ได้รับการยอมรับ เป็นวิธีเดียวที่แท้จริงในการลบคอลัมน์ข้อมูลประจำตัวโดยไม่ต้องย้ายข้อมูลจำนวนมาก
Vaccano

ว้าว! นี่คือความฉลาดมาก
Andrew Steitz

คำตอบนี้เป็นปรากฎการณ์! ขอบคุณ!
จอน

5
หากใช้ SQL Management Studio เพื่อเขียนสคริปต์ออกจากตารางอย่าลืมเปิด Tools> Options> SQL Server Object Explorer> Scripting> Table and view options> Script indexes (False by default)
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
58

ขอบคุณสำหรับคำตอบ! ช่วยฉันได้มาก! คุณควรเพิ่มการตรวจสอบสำหรับดัชนีที่อ้างอิงคอลัมน์ สิ่งที่ชอบ: 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 เป็น คอลัมน์ที่ 1 ในตารางนั้น มีวิธีแก้ปัญหาอย่างไร?
Ashish Shukla

19

ฉันเพิ่งมีปัญหาเดียวกันนี้ 4 คำสั่งใน SSMS แทนที่จะใช้ GUI และมันเร็วมาก

  • สร้างคอลัมน์ใหม่

    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

สคริปต์ต่อไปนี้จะลบฟิลด์ Identity สำหรับคอลัมน์ชื่อ 'Id'

หวังว่าจะช่วยได้

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

รหัสร้องการทำงานเป็นดีเมื่อเราไม่ทราบชื่อคอลัมน์ประจำ

ต้องคัดลอกข้อมูลลงในตารางชั่วคราวเช่น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
รัก YAAAAAAAAAA สิ่งที่ฉันกำลังมองหาวิธีง่ายๆในการหลีกเลี่ยงการสร้าง "IDENTITY" จาก "SELECT * INTO" (เพื่อสร้างตารางชั่วคราวใหม่สำหรับการสำรองข้อมูลที่ต้องการ)
KurzedMetal

มันเล็กและสวยงาม;)
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'

อย่างไรก็ตามโค้ดด้านบนจะใช้ได้เฉพาะในกรณีที่ไม่มีความสัมพันธ์ของคีย์หลักกับคีย์ต่างประเทศ


1

สำหรับคนที่มีปัญหาเดียวกันกับฉัน หากคุณต้องการแทรกเพียงครั้งเดียวคุณสามารถทำสิ่งนี้ได้

สมมติว่าคุณมีตารางที่มีสองคอลัมน์

ID Identity (1,1) | Name Varchar

และต้องการแทรกแถวด้วย ID = 4 ดังนั้นคุณ Reseed เป็น 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 คุณสามารถเปิดและปิดการแทรกข้อมูลประจำตัวได้ดังนี้:

SET IDENTITY_INSERT table_name ON

- เรียกใช้คำถามของคุณที่นี่

SET IDENTITY_INSERT table_name ปิด

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.