将标识添加到现有列

Kir*_*ein 421 sql sql-server alter-table identity-column

我需要将表的主键更改为标识列,并且表中已有许多行.

我有一个脚本来清理ID以确保它们从1开始顺序启动,在我的测试数据库上运行正常.

什么是将命令改为具有标识属性的SQL命令?

Joh*_*som 456

您无法更改现有列的标识.

你有2个选择,

  1. 使用标识创建一个新表并删除现有表

  2. 使用标识创建新列并删除现有列

方法1.(新表)您可以在此处保留新创建的标识列上的现有数据值.

CREATE TABLE dbo.Tmp_Names
    (
      Id int NOT NULL
             IDENTITY(1, 1),
      Name varchar(50) NULL
    )
ON  [PRIMARY]
go

SET IDENTITY_INSERT dbo.Tmp_Names ON
go

IF EXISTS ( SELECT  *
            FROM    dbo.Names ) 
    INSERT  INTO dbo.Tmp_Names ( Id, Name )
            SELECT  Id,
                    Name
            FROM    dbo.Names TABLOCKX
go

SET IDENTITY_INSERT dbo.Tmp_Names OFF
go

DROP TABLE dbo.Names
go

Exec sp_rename 'Tmp_Names', 'Names'
Run Code Online (Sandbox Code Playgroud)

方法2(新列)您无法在新创建的标识列上保留现有数据值,标识列将保留数字序列.

Alter Table Names
Add Id_new Int Identity(1, 1)
Go

Alter Table Names Drop Column ID
Go

Exec sp_rename 'Names.Id_new', 'ID', 'Column'
Run Code Online (Sandbox Code Playgroud)

有关更多详细信息,请参阅以下Microsoft SQL Server论坛帖子:

如何将列更改为标识(1,1)

  • 如果表数据很小,则此选项适用于gret.如果table很大,我还有另一个选择:使用ALTER TABLE ... SWITCH将表模式替换为另一个带有IDENTITY列但版本相同的版本.ALTER TABLE .... SWITCH方法的优点是它可以快速完成(对于十亿行表,在5秒内完成),因为不需要复制或更改表数据.但是有一些警告和限制.请参阅下面的答案了解详情. (46认同)
  • @Justin Grat:一个非常有趣的替代方案,我没有考虑过!这样做的原因是因为IDENTITY是列属性而不是数据类型,因此SWITCH方法将两个表(旧表和新表)之间的模式验证为可识别,而与IDENTITY差异无关.感谢分享! (6认同)

Jus*_*ant 200

在SQL 2005及更高版本中,可以在不更改表的数据页的情况下解决此问题.这对于触摸每个数据页可能需要几分钟或几小时的大型表非常重要.特技也适用即使标识列是主键,是一个聚集或非聚集索引,或可绊倒的简单的"添加/删除/重命名列"溶液中的其它陷阱的一部分.

下面是技巧:您可以使用SQL Server的ALTER TABLE ... SWITCH语句来更改表的模式而不更改数据,这意味着您可以使用具有相同表模式但没有IDENTITY列的IDENTITY替换表.同样的技巧可以将IDENTITY添加到现有列.

通常,ALTER TABLE ... SWITCH用于有效地用新的空分区替换分区表中的完整分区.但它也可以用在非分区表中.

我用这个技巧在5秒内将IDENTITY中25亿行表中的一列转换为非IDENTITY(为了运行一个多小时查询,其查询计划更适合非IDENTITY列),然后在不到5秒的时间内恢复IDENTITY设置.

这是一个如何工作的代码示例.

 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; 
Run Code Online (Sandbox Code Playgroud)

这显然比其他答案中的解决方案更复杂,但如果你的桌子很大,这可以真正节省生命.有一些警告:

  • 据我所知,使用此方法,您可以使用此方法更改有关表格列的唯一信息.不允许添加/删除列,更改可为空性等.
  • 在进行切换之前,您需要删除foriegn密钥并在之后恢复它们.
  • 对于WITH SCHEMABINDING函数,视图等也是如此
  • 新表的索引需要完全匹配(相同的列,相同的顺序等)
  • 旧表和新表需要位于同一文件组中.
  • 仅适用于SQL Server 2005或更高版本
  • 我之前认为这个技巧仅适用于SQL Server的企业版或开发者版(因为分区仅在Enterprise和Developer版本中支持),但Mason G. Zhwiti在下面的评论中说它也适用于SQL标准版.我认为这意味着对Enterprise或Developer的限制不适用于ALTER TABLE ... SWITCH.

TechNet上有一篇很好的文章详细说明了上述要求.

更新 - Eric Wu在下面发表评论,增加了有关此解决方案的重要信息.在此处复制以确保它得到更多关注:

这里还有另一个值得一提的警告.虽然新表将很乐意从旧表中接收数据,并且所有新行将按照标识模式插入,但它们将从1开始并且如果所述列是主键则可能会中断.考虑DBCC CHECKIDENT('<newTableName>')切换后立即运行.有关详细信息,请参阅msdn.microsoft.com/en-us/library/ms176057.aspx.

如果表正在积极与新行扩展(这意味着你没有太多的如果添加的身份和添加新行,然后代替之间的任何停机时间DBCC CHECKIDENT,你会想手动设置新表模式的身份种子值是大于表中最大的现有ID,例如IDENTITY (2435457, 1),您可能能够在事务中包含ALTER TABLE...SWITCHDBCC CHECKIDENT(或者没有 - 没有测试过),但似乎手动设置种子值将更容易和更安全.

显然,如果没有新的行添加到表中(或者它们只是偶尔添加,就像每日ETL过程一样),那么这种竞争条件不会发生,所以DBCC CHECKIDENT很好.

  • 如果我的记忆是正确的,我从这篇文章中得到了一个想法:http://www.sqlservercentral.com/articles/T-SQL/61979/ (4认同)
  • @jbatista - OP的问题表明他已经在桌面上有一个主键,并且已经可以确保正确的值,但他只是想将其更改为IDENTITY列.我上面的回答集中在狭窄的用例:如何将IDENTITY添加到列而不实际更改任何数据.我上面记录的方法对于大型表来说是一个巨大的节省时间.如果您需要更改数据,则需要使用其他解决方案. (3认同)
  • 这里还有另一个值得一提的警告.虽然新表将很乐意从旧表中接收数据,并且所有新行将按照标识模式插入,*它们将从1*开始,如果所述列是主键,则可能会中断.考虑在切换后立即运行`DBCC CHECKIDENT('<newTableName>')`.有关详细信息,请参阅https://msdn.microsoft.com/en-us/library/ms176057.aspx. (3认同)
  • 这是一个很好的答案!另请注意,列的可为空性必须相同.因此,如果您需要更改列的可为空性,则必须在以后的步骤中执行此操作.PK约束也是如此.我还更改了表创建中的标识值以匹配当前最大值:IDENTITY(maxID + 1,1) (3认同)
  • 仅供参考,这似乎也适用于SQL 2008 R2的标准版本.也许他们启用此功能就像他们现在启用了打开备份压缩的能力一样. (2认同)
  • 梅森 - 很棒的观察!我相应地更新了答案.我假设,因为SQL Standard不支持分区,也不支持ALTER TABLE ... SWITCH.但鉴于您可以在非分区表上使用此命令,您还可以在Standard Edition上运行它.谢谢你的纠正. (2认同)

mar*_*c_s 66

您不能将列更改为IDENTITY列.您需要做的是创建一个新列,该列从一开始就定义为IDENTITY,然后删除旧列,并将新列重命名为旧名称.

ALTER TABLE (yourTable) ADD NewColumn INT IDENTITY(1,1)

ALTER TABLE (yourTable) DROP COLUMN OldColumnName

EXEC sp_rename 'yourTable.NewColumn', 'OldColumnName', 'COLUMN'
Run Code Online (Sandbox Code Playgroud)

  • 如果此列是约束,则它不起作用 (12认同)
  • 失败的是sp_rename过程。我通过搜索错误文本找到了关于stackoverflow的解决方案。尽管我的表名称中没有任何特殊字符,但这似乎是一些带有括号的严格语法规则。 (2认同)

gre*_*man 14

这里描述了很酷的解决方案: SQL SERVER - 在列上添加或删除标识属性

简而言之,在SQL Manager中手动编辑表,切换标识,不保存更改,只显示将为更改创建的脚本,复制并稍后使用.

它节省了大量时间,因为它(脚本)包含与您更改的表相关的所有外键,索引等.手动写这个......上帝保佑.

  • 可能对某人有帮助。获取更改后的更改脚本。在 SSMS 上的设计模式下右键单击表,然后选择选项“生成更改脚本”并将脚本保存在本地驱动器中 (2认同)

OTA*_*TAR 7

考虑使用SEQUENCE代替IDENTITY

在sql server 2014中(我不知道较低的版本),您可以使用序列简单地做到这一点。

CREATE SEQUENCE  sequence_name START WITH here_higher_number_than_max_existed_value_in_column INCREMENT BY 1;

ALTER TABLE table_name ADD CONSTRAINT constraint_name DEFAULT NEXT VALUE FOR sequence_name FOR column_name
Run Code Online (Sandbox Code Playgroud)

从这里开始:序列作为列的默认值


Sat*_*Che 6

简单的解释

使用sp_RENAME重命名现有列

EXEC sp_RENAME'Table_Name.Existing_ColumnName','New_ColumnName','COLUMN'

重命名示例:

现有列UserID重命名为OldUserID

EXEC sp_RENAME 'AdminUsers.UserID' , 'OldUserID', 'COLUMN'
Run Code Online (Sandbox Code Playgroud)

然后使用alter query添加新列以设置为主键和标识值

ALTER TABLE TableName ADD Old_ColumnName INT NOT NULL PRIMARY KEY IDENTITY(1,1)
Run Code Online (Sandbox Code Playgroud)

设置主键的示例

新创建的列名称是UserID

ALTER TABLE Users ADD UserID INT NOT NULL PRIMARY KEY IDENTITY(1,1)
Run Code Online (Sandbox Code Playgroud)

然后删除重命名列

ALTER TABLE Table_Name DROP COLUMN Renamed_ColumnName
Run Code Online (Sandbox Code Playgroud)

Drop重命名列的示例

ALTER TABLE Users DROP COLUMN OldUserID
Run Code Online (Sandbox Code Playgroud)

现在我们将主键和标识添加到表中的现有列.


Jam*_*ard 5

我是一名Java开发人员,碰巧加入了一个没有DBA的团队,而作为开发人员,我却无法获得DBA权利。我的任务是在两个数据库之间移动整个架构,因此,没有DBA,我必须通过运行脚本来做到这一点,并且不能在SQL Server 2008中使用GUI,因为我没有管理员权限。

一切都顺利进行,但是,在新的schema.table上运行存储过程时,我发现我丢失了表中的identity字段。我仔细检查了创建表的脚本,它在那里,但是,当我运行脚本时,SQL Server没有得到它。后来,DBA告诉我他以前也遇到过同样的问题。

无论如何,对于SQL Server 2008,这些都是我为解决此问题而采取的步骤,并且它们起作用了,因此,我将其发布在此处,希望对您有所帮助。这是我做的,因为我对另一个表具有FK依赖关系,这使此操作变得更加困难:

我使用此查询来验证身份确实丢失,并查看对表的依赖关系。

1.)在表格上查找统计信息:

exec sp_help 'dbo.table_name_old';
Run Code Online (Sandbox Code Playgroud)

2.)创建一个重复的,相同的新表,不同之处在于,在以前的PK字段上添加一个身份字段。

3.)禁用身份以移动数据。

SET IDENTITY_INSERT dbo.table_name ON 
Run Code Online (Sandbox Code Playgroud)

4.)传输数据。

INSERT INTO dbo.table_name_new
(
field1, field2, etc...
)
SELECT 
field1, field2, etc...
FROM 
dbo.table_name_old;
Run Code Online (Sandbox Code Playgroud)

5.)确认数据在那里。

SELECT * FROM dbo.table_name_new
Run Code Online (Sandbox Code Playgroud)

6.)重新启用身份。

SET IDENTITY_INSERT ToyRecP.ToyAwards.lkpFile_New OFF
Run Code Online (Sandbox Code Playgroud)

7.)这是我发现的最好的脚本,它可以获取所有FK关系以验证原始表引用了哪些表作为依赖关系,并且遇到了很多表,因此它是管理员!

SELECT f.name AS ForeignKey,
   OBJECT_NAME(f.parent_object_id) AS TableName,
   COL_NAME(fc.parent_object_id, fc.parent_column_id) AS ColumnName,
   OBJECT_NAME (f.referenced_object_id) AS ReferenceTableName,
   COL_NAME(fc.referenced_object_id, fc.referenced_column_id) AS ReferenceColumnName
FROM sys.foreign_keys AS f
INNER JOIN sys.foreign_key_columns AS fc
   ON f.OBJECT_ID = fc.constraint_object_id
   ORDER BY ReferenceTableName;
Run Code Online (Sandbox Code Playgroud)

8.)在下一步之前,请确保您具有所有涉及的表的所有PK和FK脚本。

9.)您可以右键单击每个键,然后使用SQL Server 2008对此脚本编写脚本

10.)使用以下语法从依赖关系表中删除FK:

ALTER TABLE [dbo].[table_name] DROP CONSTRAINT [Name_of_FK]
Run Code Online (Sandbox Code Playgroud)

11.)删除原始表:

DROP TABLE dbo.table_name_old;
Run Code Online (Sandbox Code Playgroud)

13.)接下来的步骤取决于您在步骤9中在SQL Server 2008中创建的脚本。

-将PK添加到新表。

-将FK添加到新表中。

-将FK添加回依赖表。

14.)验证一切正确和完整。我使用GUI查看表。

15.)将新表重命名为原始表名。

exec sp_RENAME '[Schema_Name.OldTableName]' , '[NewTableName]';
Run Code Online (Sandbox Code Playgroud)

最后,一切正常!


Rin*_*nku 5

据我了解,在正常情况下,我们正在创建一个带有具有Identity 属性的主键的表 ,因此重命名删除与主键约束关联的列将是不可能的,因为约束规则正在验证列结构。 要实现此目的,我们必须按以下方式处理一些步骤: 假设TableName = 'Employee'ColumnName = 'EmployeeId' 1. 在 'Employee' 表中添加新列 'EmployeeId_new' ALTER TABLE Employee ADD EmployeeId_new INT IDENTITY( 1,1)





  1. 现在从“Employee”表中删除“EmployeeId”列
    ALTER TABLE Employee DROP COLUMN EmployeeId

  2. 这将引发错误,因为主键约束规则适用并验证列结构。
    *### '消息 5074,级别 16,状态 1,第 1 行 对象 [PK_dbo.Employee] 依赖于列 [EmployeeId]。' ###

  3. 因此,我们必须首先从表“Employee”中删除主键约束,然后才能删除列
    ALTER TABLE Employee DROP 约束 [PK_dbo.Employee]

  4. 现在我们可以从“Employee”表中删除“EmployeeId”列,就像上一步中出现错误
    ALTER TABLE Employee DROP COLUMN EmployeeId一样

  5. 现在列“EmployeeId”已从表中删除因此我们将用“EmployeeId”重命名新添加的新列“EmployeeId_new”
    sp_rename“Employee.EmployeeId”、“EmployeeId_new”、“COLUMN”

  6. 要以与以前相同的形式重新排列表,我们必须为“EmployeeId”列添加主键约束
    ALTER TABLE Employee 添加约束 [PK_dbo.Employee] 主键 (EmployeeId)

8.现在,带有“EmployeeId”的表“Employee”已根据身份规则以及现有主键约束进行修改