从表中的列中删除身份


127

我们有一个5GB的表(近5亿行),我们想删除其中一列的identity属性,但是当我们尝试通过SSMS执行此操作时,它就会超时。

可以通过T-SQL完成吗?


1
您可以在此处发布表的架构吗?
Al W

我敢肯定,SQL Server不支持通过简单的ALTER TABLE ...语句从列中删除标识属性,这是有充分的理由的,但是目前让我感到遗憾的是这种情况。
乔恩·施耐德

Answers:


143

设置后就无法删除IDENTITY规范。

要删除整个列:

ALTER TABLE yourTable
DROP COLUMN yourCOlumn;

此处有关ALTER TABLE的信息

如果需要保留数据,但要删除IDENTITY列,则需要:

  • 创建一个新列
  • 将数据从现有IDENTITY列传输到新列
  • 删除现有IDENTITY列。
  • 将新列重命名为原始列名

3
您可以删除身份规范。实际上,我昨天必须使用SSMS进行此操作,尽管不是在5亿行上。
西蒙

33
@simon如果您对更改进行脚本编写,您会看到SSMS实际上是在创建表的副本而没有identity属性。
代码魔术师

3
我只想添加将新列重命名为原始列的名称。另外,如果将此identity列用作foreign key另一个表中a的一部分,则必须首先删除约束,然后按照@AdamWenger提到的有关删除标识的attribute/property步骤进行操作。您还可以查看此链接以了解更多详细信息关于仅删除属性:blog.sqlauthority.com/2009/05/03/… ..祝您好运!
Nonym

1
对于拒绝投票的人-听到您对我的回答不满意,我将不胜感激。
亚当·温格

1
看看下面的马克·索沃(Mark Sowul)的答案。无需在列之间移动数据。使用Mark的答案,您只是在拖曳元数据。除非您正在处理具有数以千万计的行的表,否则没什么大不了的。加上Mark的答案可防止在表模式中重新定位列。我只是尝试了一下,它就像一种魅力。非常聪明。
安德鲁·斯蒂兹

100

如果您要执行此操作而又不添加和填充新列不对进行重新排序以及几乎没有停机时间(因为表上没有数据发生变化),那么让我们对分区功能进行一些魔术处理(但是由于没有使用分区,因此您无需这样做)需要企业版):

  1. 删除所有指向该表的外键
  2. 编写要创建的表的脚本;重命名所有内容,例如“ MyTable2”,“ MyIndex2”等。删除IDENTITY规范。
  3. 现在,您应该有两个“相同”的表,一个完整,另一个为空,没有身份。
  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

11
这应该是公认的答案。这是在不进行大量数据迁移的情况下删除标识列的唯一真正方法。
瓦卡诺

哇!这非常聪明。
安德鲁·斯蒂兹

这个答案是惊人的!谢谢!
乔恩

5
如果使用SQL Management Studio编写表脚本,请确保打开“工具”>“选项”>“ SQL Server对象资源管理器”>“脚本”>“表和视图选项”>“脚本索引”(默认为False)
user423430 2016年

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约束,删除该列,重命名temp列,然后将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);
..

El Fin!


注意您的sql服务器版本,我在azure sql服务器中尝试过,并不是所有的操作都在azure版本sql中受支持。
纵火犯,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 2015年

一个很好的技巧,对我帮助很大,但是,我要在这里提到的一点是,当我们按照第一步中所述添加新的重复列时,它在末尾添加了,但是通常我们希望主键列即Id为该表的第一列。那么有什么解决方法吗?
Ashish Shukla

19

我只是有同样的问题。在SSMS中使用4条语句而不是使用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

以下脚本删除名为“ 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。正是我在寻找的一种简单方法,可以避免从“ SELECT * INTO”生成“ IDENTITY”(为需要的备份创建新的表临时文件)
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的行。因此将其重新设置为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开启

-在这里运行您的查询

SET IDENTITY_INSERT table_name关闭

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.