向现有列添加标识

我需要将一个表的主键更改为一个标识列,并且在表中已经有许多行。

我有一个脚本来清理id,以确保它们从1开始是顺序的,在我的测试数据库上运行良好。

更改列以具有标识属性的SQL命令是什么?

1213812 次浏览

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

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


ALTER TABLE (yourTable) DROP COLUMN OldColumnName


EXEC sp_rename 'yourTable.NewColumn', 'OldColumnName', 'COLUMN'

马克

我不相信您可以使用tsql将现有列更改为标识列。但是,您可以通过Enterprise Manager设计视图来完成。

或者,您可以创建一个新行作为标识列,删除旧列,然后重命名新列。

ALTER TABLE FooTable
ADD BarColumn INT IDENTITY(1, 1)
NOT NULL
PRIMARY KEY CLUSTERED

遗憾的是,没有一个;IDENTITY属性属于表而不是列。

更简单的方法是在GUI中完成,但如果没有这个选项,则可以复制数据、删除列、用标识重新添加它,然后放回数据。

请参阅在这里详细说明。

您不能更改现有列的标识。

你有两个选择,

  1. 创建一个标识为&的新表;删除现有的表

  2. 创建一个新的列与标识&删除现有列

方法1。在这里,您可以保留新创建的标识列上的现有数据值。请注意,如果不满足'if not exists',您将丢失所有数据,因此请确保您将条件也放在drop上!

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'

方法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'

请参阅以下Microsoft SQL Server论坛帖子了解更多细节:

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

根据设计,没有简单的方法来打开或关闭现有列的标识特性。要做到这一点,唯一干净的方法是创建一个新列并使其成为标识列,或者创建一个新表并迁移数据。

如果我们使用SQL Server Management Studio去除列“id”上的标识值,则会创建一个新的临时表,数据被移动到临时表中,旧表被删除,新表被重命名。

使用Management Studio进行更改,然后在设计器中右键单击并选择“生成更改脚本”。

你会看到这就是SQL server在后台所做的。

您不能这样做,您需要添加另一列,删除原来的列并重命名新列,或者创建一个新表,复制数据并删除旧表,然后将新表重命名为旧表

如果你使用SSMS并在设计器中将标识属性设置为ON,下面就是SQL Server在幕后所做的事情。因此,如果你有一个名为[user]的表,如果你设置UserID和identity,就会发生这样的情况

BEGIN TRANSACTION
SET QUOTED_IDENTIFIER ON
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
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


GO
CREATE TABLE dbo.Tmp_User
(
UserID int NOT NULL IDENTITY (1, 1),
LastName varchar(50) NOT NULL,
FirstName varchar(50) NOT NULL,
MiddleInitial char(1) NULL


)  ON [PRIMARY]
GO


SET IDENTITY_INSERT dbo.Tmp_User ON
GO
IF EXISTS(SELECT * FROM dbo.[User])
EXEC('INSERT INTO dbo.Tmp_User (UserID, LastName, FirstName, MiddleInitial)
SELECT UserID, LastName, FirstName, MiddleInitialFROM dbo.[User] TABLOCKX')
GO
SET IDENTITY_INSERT dbo.Tmp_User OFF
GO


GO
DROP TABLE dbo.[User]
GO
EXECUTE sp_rename N'dbo.Tmp_User', N'User', 'OBJECT'
GO
ALTER TABLE dbo.[User] ADD CONSTRAINT
PK_User PRIMARY KEY CLUSTERED
(
UserID
) ON [PRIMARY]


GO
COMMIT

已经说过,有一种方法可以通过设置位值来破解系统表,但这是不支持的,我不会这么做

在SQL 2005及以上版本中,有一个技巧可以在不改变表的数据页的情况下解决这个问题。这对于大型表很重要,因为在这些表中,处理每个数据页可能需要花费几分钟或几小时的时间。即使标识列是一个主键,是聚集或非聚集索引的一部分,或者其他容易出错的“添加/删除/重命名列”,这个技巧也同样有效。解决方案。

这里有一个技巧:您可以使用SQL Server的ALTER TABLE…开关语句在不改变数据的情况下更改表的模式,这意味着您可以使用相同的表模式替换IDENTITY表,但没有IDENTITY列。同样的技巧也适用于向现有列添加IDENTITY。

通常,ALTER TABLE…开关用于有效地将分区表中的完整分区替换为新的空分区。但它也可以用于非分区表。

我使用这个技巧在不到5秒的时间内将一个25亿行表中的列从IDENTITY转换为非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;

这显然比其他答案更复杂,但如果您的表很大,这可能是一个真正的救星。这里有一些注意事项:

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

有一个很好的文章来源:TechNet详细说明了上述需求。

更新- 埃里克·吴下面有一个评论,增加了关于这个解决方案的重要信息。复制在这里,以确保它得到更多的关注:

这里还有一个值得注意的警告。虽然 新表将愉快地从旧表接收数据,并且所有的 新行将按照标识模式插入 从1开始,如果该列是主键,则可能中断。 考虑立即运行DBCC CHECKIDENT('<newTableName>') 切换。参见msdn.microsoft.com/en-us/library/ms176057.aspx了解更多信息 信息。< / p >

如果表正在积极地用新行扩展(这意味着在添加IDENTITY和添加新行之间没有太多停机时间,那么您将需要手动在新表模式中设置标识种子值,使其大于表中最大的现有ID,例如IDENTITY (2435457, 1)。您可以在事务中同时包含ALTER TABLE...SWITCHDBCC CHECKIDENT(或者没有——还没有测试过这个),但手动设置种子值似乎更容易和更安全。

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

这里有一个很酷的解决方案: # EYZ0 < / p >

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

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

如果最初的海报实际上是想要设置一个现有的列为PRIMARY KEY表,实际上不需要列为IDENTITY列(两个不同的东西),那么这可以通过t-SQL完成:

ALTER TABLE [YourTableName]
ADD CONSTRAINT [ColumnToSetAsPrimaryKey] PRIMARY KEY ([ColumnToSetAsPrimaryKey])

请注意列名周围的括号在PRIMARY KEY选项之后。

虽然这篇文章是旧的,我只是对请求者的需求做了一个假设,但我觉得这些额外的信息可能会对遇到这个线程的用户有所帮助,因为我相信对话可能会导致一个人认为,如果不先将现有的列添加为新列,就不能将其设置为主键,这是不正确的。

我是一名java开发人员,碰巧加入了一个没有DBA的团队,而作为一名开发人员,我无法获得DBA权限。我的任务是在两个数据库之间移动整个模式,所以没有DBA,我必须通过运行脚本来完成,不能使用SQL Server 2008中的GUI,因为我没有管理权限。

但是,在新模式上运行存储过程时,所有内容都被移动了。表,我发现我丢失了一个表的标识字段。我仔细检查了创建表的脚本,它在那里,但是,SQL Server在我运行脚本时没有得到它。后来,一位DBA告诉我,他以前见过同样的问题。

在任何情况下,对于SQL Server 2008,这些是我采取的步骤来解决这个问题,他们工作,所以我张贴在这里,希望它会对某人有所帮助。这是我所做的,因为我对另一个表有FK依赖,这使得这更困难:

我使用这个查询来验证标识确实缺失,并查看表上的依赖关系。

1)。查找表上的统计信息:

exec sp_help 'dbo.table_name_old';

2)。创建一个重复的、完全相同的新表,只是在原来的PK字段上添加了一个标识字段。

3)。禁用标识以移动数据。

SET IDENTITY_INSERT dbo.table_name ON

4)。传输数据。

INSERT INTO dbo.table_name_new
(
field1, field2, etc...
)
SELECT
field1, field2, etc...
FROM
dbo.table_name_old;

5)。验证数据是否存在。

SELECT * FROM dbo.table_name_new

6)。重新启用标识。

SET IDENTITY_INSERT ToyRecP.ToyAwards.lkpFile_New OFF

7.) 这是我找到的最好的脚本,以获得所有FK关系,以验证原始表引用的依赖关系 我遇到过很多,所以它是一个保存者!< /强> < / p >

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;

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

9)。您可以右键单击每个键并使用SQL Server 2008编写脚本

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

ALTER TABLE [dbo].[table_name] DROP CONSTRAINT [Name_of_FK]

11)。删除原来的表:

DROP TABLE dbo.table_name_old;

13)。接下来的步骤依赖于您在SQL Server 2008第9步中创建的脚本。

将PK添加到新表中。

——将FK添加到新表中。

——将FK添加回依赖项表。

14)。验证所有内容是否正确和完整。我使用GUI查看表格。

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

exec sp_RENAME '[Schema_Name.OldTableName]' , '[NewTableName]';

最后,一切都成功了!

基本上有四个逻辑步骤。

  1. 创建一个新的Identity列。为这个新列打开Insert Identity。

  2. 将源列(希望转换为Identity的列)中的数据插入到这个新列中。

  3. 关闭新列的Insert Identity。

  4. 删除源列&将新列重命名为源列的名称。

可能会有一些更复杂的事情,比如跨多个服务器工作等。

有关步骤,请参阅下面的文章(使用ssms &t - sql)。这些步骤适用于不太熟悉T-SQL的初学者。

http://social.technet.microsoft.com/wiki/contents/articles/23816.how-to-convert-int-column-to-identity-in-the-ms-sql-server.aspx

为所有没有标识集的主键= bigint的表生成脚本;这将返回每个表的生成脚本列表;

SET NOCOUNT ON;


declare @sql table(s varchar(max), id int identity)


DECLARE @table_name nvarchar(max),
@table_schema nvarchar(max);


DECLARE vendor_cursor CURSOR FOR
SELECT
t.name, s.name
FROM sys.schemas AS s
INNER JOIN sys.tables AS t
ON s.[schema_id] = t.[schema_id]
WHERE EXISTS (
SELECT
[c].[name]
from sys.columns [c]
join sys.types [y] on [y].system_type_id = [c].system_type_id
where [c].[object_id] = [t].[object_id] and [y].name = 'bigint' and [c].[column_id] = 1
) and NOT EXISTS
(
SELECT 1 FROM sys.identity_columns
WHERE [object_id] = t.[object_id]
) and exists (
select 1 from sys.indexes as [i]
inner join sys.index_columns as [ic]  ON  i.OBJECT_ID = ic.OBJECT_ID AND i.index_id = ic.index_id
where object_name([ic].[object_id]) = [t].[name]
)
OPEN vendor_cursor


FETCH NEXT FROM vendor_cursor
INTO @table_name, @table_schema


WHILE @@FETCH_STATUS = 0
BEGIN


DELETE FROM @sql


declare @pkname varchar(100),
@pkcol nvarchar(100)


SELECT  top 1
@pkname = i.name,
@pkcol = COL_NAME(ic.OBJECT_ID,ic.column_id)
FROM    sys.indexes AS [i]
INNER JOIN sys.index_columns AS [ic] ON  i.OBJECT_ID = ic.OBJECT_ID AND i.index_id = ic.index_id
WHERE   i.is_primary_key = 1 and OBJECT_NAME(ic.OBJECT_ID) = @table_name


declare @q nvarchar(max) = 'SELECT  '+@pkcol+' FROM ['+@table_schema+'].['+@table_name+'] ORDER BY '+@pkcol+' DESC'


DECLARE @ident_seed nvarchar(max) -- Change this to the datatype that you are after
SET @q = REPLACE(@q, 'SELECT', 'SELECT TOP 1 @output = ')
EXEC sp_executeSql @q, N'@output bigint OUTPUT', @ident_seed OUTPUT


insert into  @sql(s) values ('BEGIN TRANSACTION')
insert into  @sql(s) values ('BEGIN TRY')


-- create statement
insert into  @sql(s) values ('create table ['+@table_schema+'].[' + @table_name + '_Temp] (')


-- column list
insert into @sql(s)
select
'  ['+[c].[name]+'] ' +
y.name +


(case when [y].[name] like '%varchar' then
coalesce('('+(case when ([c].[max_length] < 0 or [c].[max_length] >= 1024) then 'max' else cast([c].max_length as varchar) end)+')','')
else '' end)


+ ' ' +
case when [c].name = @pkcol then 'IDENTITY(' +COALESCE(@ident_seed, '1')+',1)' else '' end + ' ' +
( case when c.is_nullable = 0 then 'NOT ' else '' end ) + 'NULL ' +
coalesce('DEFAULT ('+(
REPLACE(
REPLACE(
LTrim(
RTrim(
REPLACE(
REPLACE(
REPLACE(
REPLACE(
LTrim(
RTrim(
REPLACE(
REPLACE(
object_definition([c].default_object_id)
,' ','~')
,')',' ')
)
)
,' ','*')
,'~',' ')
,' ','~')
,'(',' ')
)
)
,' ','*')
,'~',' ')
) +
case when object_definition([c].default_object_id) like '%get%date%' then '()' else '' end
+
')','') + ','
from sys.columns c
JOIN sys.types y ON y.system_type_id = c.system_type_id
where OBJECT_NAME(c.[object_id]) = @table_name and [y].name != 'sysname'
order by [c].column_id




update @sql set s=left(s,len(s)-1) where id=@@identity


-- closing bracket
insert into @sql(s) values( ')' )


insert into @sql(s) values( 'SET IDENTITY_INSERT ['+@table_schema+'].['+@table_name+'_Temp] ON')


declare @cols nvarchar(max)
SELECT @cols = STUFF(
(
select ',['+c.name+']'
from sys.columns c
JOIN sys.types y ON y.system_type_id = c.system_type_id
where c.[object_id] = OBJECT_ID(@table_name)
and [y].name != 'sysname'
and [y].name != 'timestamp'
order by [c].column_id
FOR XML PATH ('')
)
, 1, 1, '')


insert into @sql(s) values( 'IF EXISTS(SELECT * FROM ['+@table_schema+'].['+@table_name+'])')
insert into @sql(s) values( 'EXEC(''INSERT INTO ['+@table_schema+'].['+@table_name+'_Temp] ('+@cols+')')
insert into @sql(s) values( 'SELECT '+@cols+' FROM ['+@table_schema+'].['+@table_name+']'')')


insert into @sql(s) values( 'SET IDENTITY_INSERT ['+@table_schema+'].['+@table_name+'_Temp] OFF')




insert into @sql(s) values( 'DROP TABLE ['+@table_schema+'].['+@table_name+']')


insert into @sql(s) values( 'EXECUTE sp_rename N''['+@table_schema+'].['+@table_name+'_Temp]'', N'''+@table_name+''', ''OBJECT''')


if ( @pkname is not null ) begin
insert into @sql(s) values('ALTER TABLE ['+@table_schema+'].['+@table_name+'] ADD CONSTRAINT ['+@pkname+'] PRIMARY KEY CLUSTERED (')
insert into @sql(s)
select '  ['+COLUMN_NAME+'] ASC,' from information_schema.key_column_usage
where constraint_name = @pkname
GROUP BY COLUMN_NAME, ordinal_position
order by ordinal_position


-- remove trailing comma
update @sql set s=left(s,len(s)-1) where id=@@identity
insert into @sql(s) values ('  )')
end


insert into  @sql(s) values ('--Run your Statements')
insert into  @sql(s) values ('COMMIT TRANSACTION')
insert into  @sql(s) values ('END TRY')
insert into  @sql(s) values ('BEGIN CATCH')
insert into  @sql(s) values ('        ROLLBACK TRANSACTION')
insert into  @sql(s) values ('        DECLARE @Msg NVARCHAR(MAX)  ')
insert into  @sql(s) values ('        SELECT @Msg=ERROR_MESSAGE() ')
insert into  @sql(s) values ('        RAISERROR(''Error Occured: %s'', 20, 101,@msg) WITH LOG')
insert into  @sql(s) values ('END CATCH')


declare @fqry nvarchar(max)


-- result!
SELECT @fqry = (select char(10) + s from @sql order by id FOR XML PATH (''))




SELECT @table_name as [Table_Name], @fqry as [Generated_Query]
PRINT 'Table: '+@table_name
EXEC sp_executeSql @fqry


FETCH NEXT FROM vendor_cursor
INTO @table_name, @table_schema
END
CLOSE vendor_cursor;
DEALLOCATE vendor_cursor;

简单的解释

使用sp_RENAME重命名现有列

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

重命名示例:

现有列UserID被重命名为OldUserID

EXEC sp_RENAME 'AdminUsers.UserID' , 'OldUserID', 'COLUMN'

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

ALTER TABLE TableName ADD Old_ColumnName INT NOT NULL PRIMARY KEY IDENTITY(1,1)

使用实例设置主键

新创建的列名为UserID

ALTER TABLE Users ADD UserID INT NOT NULL PRIMARY KEY IDENTITY(1,1)

然后删除重命名列

ALTER TABLE Table_Name DROP COLUMN Renamed_ColumnName

删除重命名列的示例

ALTER TABLE Users DROP COLUMN OldUserID

现在,我们向表上的现有列添加了一个主键和标识。

考虑使用序列代替恒等式

在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

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

正如我所理解的,在正常情况下,我们正在创建一个表,主键标识属性
因此,与主键 约束相关联的重命名删除列将不可能存在,因为约束规则正在验证列结构 为了实现这一点,我们必须以以下方式处理一些步骤:
让我们假设TableName = '雇员'ColumnName = 'EmployeeId'

1. 在'Employee'表中添加新列'EmployeeId_new'
ALTER TABLE Employee ADD EmployeeId_new INT IDENTITY(1,1)

    现在从'Employee'表中删除'EmployeeId'列
    ALTER TABLE Employee DROP COLUMN EmployeeId

  1. 这将抛出错误,因为主键约束规则适用并验证列结构。
    * # # # “# EYZ0 # # # < / p > < /李> 所以我们必须首先从表'Employee'中删除主键约束,然后我们可以删除列
    ALTER TABLE Employee DROP constraint [PK_dbo.]员工)< /强> < / p >

  2. 现在,我们可以从'Employee'表中删除'EmployeeId'列,就像在我们得到错误的前一步中所做的那样
    ALTER TABLE Employee DROP COLUMN EmployeeId

  3. 现在列'EmployeeId'从表中删除 因此,我们将新添加的列EmployeeId_new重命名为EmployeeId
    sp_rename”员工。EmployeeId', 'EmployeeId_new', 'COLUMN'

  4. 要以相同的形式重新排列表,我们必须为'EmployeeId'列添加主键约束。
    ALTER TABLE员工表Employee]主键(EmployeeId)

    . log

# EYZ0 < / >强

根据我目前的情况,我采用这种方法。我想通过脚本插入数据后给一个主表的身份。

因为我想要追加身份,所以它总是从1开始到我想要的记录计数的结束。

--first drop column and add with identity
ALTER TABLE dbo.tblProductPriceList drop column ID
ALTER TABLE dbo.tblProductPriceList add ID INT IDENTITY(1,1)


--then add primary key to that column (exist option you can ignore)
IF  NOT EXISTS (SELECT * FROM sys.key_constraints  WHERE object_id = OBJECT_ID(N'[dbo].[PK_tblProductPriceList]') AND parent_object_id = OBJECT_ID(N'[dbo].[tblProductPriceList]'))
ALTER TABLE [tblProductPriceList] ADD PRIMARY KEY (id)
GO

这将创建具有identity的相同主键列

我使用这个链接:https://blog.sqlauthority.com/2014/10/11/sql-server-add-auto-incremental-identity-column-to-table-after-creating-table/

为现有表添加主键

在对象资源管理器中右键单击表名。你会得到一些选择。点击“设计”。将为该表打开一个新选项卡。您可以在“列属性”中添加标识约束。

修改列的标识属性:

  • 在服务器资源管理器中,右键单击具有要修改的标识属性的表,然后单击“打开表定义”。 表在表设计器中打开。
  • 清除要更改的列的“允许空值”复选框。
  • 在“列属性”选项卡中,展开“标识规范”属性。
  • 单击Is Identity子属性的网格单元格,并从下拉列表中选择Yes。
  • 在“标识种子”单元格中键入值。这个值将被赋给表中的第一行。默认情况下,值为1。

就是这样,对我很管用

如果你碰巧使用Visual Studio 2017+

  1. 在服务器对象资源管理器中,右键单击表格,选择“视图代码”
  2. 向列添加修饰符“IDENTITY”
  3. 更新

这将为你做的一切。