在SQL 2005及更高版本中,有一个技巧可以在不更改表的数据页的情况下解决这个问题。这对于接触每个数据页可能需要几分钟或几个小时的大型表来说是很重要的。即使Identity列是主键,是群集索引或非聚集索引的一部分,或者其他可能跳过更简单的“Add/Remove/rename列”解决方案的问题,这种技巧也是有效的。

诀窍是:您可以使用SQLServer的改变桌子.开关语句以更改表的架构而不更改数据,这意味着可以用相同的表架构替换表,但不需要标识列。同样的技巧可以将标识添加到现有列中。

正常情况下,改变桌子.开关用于使用新的空分区有效地替换已分区表中的完整分区。但它也可以用于非分区表。

我用这个技巧在5秒内将25亿行表的列从标识转换为非标识(为了运行一个查询计划对非标识列更有效的多小时查询),然后再在不到5秒内恢复标识设置。

下面是一个代码示例,说明它是如何工作的。CREATE TABLE Test (

id int identity(1,1),
somecolumn varchar(10)
);
INSERT INTO Test VALUES ('Hello');
INSERT INTO Test VALUES ('World');
-- copy the table. use same schema, but no identity
CREATE TABLE Test2 (
id int NOT NULL,
somecolumn varchar(10)
);
ALTER TABLE Test SWITCH TO Test2;
-- drop the original (now empty) table
DROP TABLE Test;
-- rename new table to old table's name
EXEC sp_rename 'Test2','Test';
-- update the identity seed
DBCC CHECKIDENT('Test');
-- see same records
SELECT * FROM Test;

这显然比其他答案中的解决方案更复杂,但是如果您的表很大,这可能是一个真正的拯救生命的方法。这里有一些警告:据我所知,身份是唯一可以使用此方法更改表列的内容。添加/删除列、更改空值等。是不允许的。

在切换之前,您需要丢弃主键,然后再恢复它们。

同样适用于SCHEMABINING功能、视图等。

新表的索引需要完全匹配(相同的列、相同的顺序等)

旧表和新表需要位于同一个文件组上。

仅适用于SQLServer 2005或更高版本

我以前认为这个技巧只适用于SQL Server的Enterprise或Developer版本(因为分区只支持Enterprise和Developer版本),但是Mason G.zhwiti在下面的评论中说,它也适用于SQL Standard Edition。我假设这意味着对Enterprise或Developer的限制不适用于ALTERTABLE.开关。

有一个很好的TechNet文章详细说明上述要求。

更新-吴立贤下面有一个注释,它添加了有关此解决方案的重要信息。在这里复制它以确保它得到更多的关注:这里还有一个值得提的警告。尽管新表将很高兴地从旧表接收数据,并且所有新行都将按照标识模式插入,但如果该列是主键,则它们将从1开始并可能中断。考虑运行DBCC CHECKIDENT('')在切换后立即。看见msdn.microsoft.com/en-us/library/ms176057.aspx想了解更多信息。

如果该表正在使用新行进行主动扩展(意味着在添加标识和添加新行之间没有太多停机时间,那么DBCC CHECKIDENT您需要手动将新表模式中的Identity种子值设置为大于表中最大的现有ID。IDENTITY (2435457, 1)..您可能可以同时包含两个ALTER TABLE...SWITCH而DBCC CHECKIDENT在事务中(或者没有-还没有测试过这一点),但是手动设置种子值似乎更容易,也更安全。

显然,如果没有新的行被添加到表中(或者只是偶尔添加它们,比如每天的ETL进程),那么这种争用条件就不会发生。DBCC CHECKIDENT都没问题。