将标识添加到现有列
我需要将表的主键更改为标识列,表中已经有许多行。
我有一个脚本来清理ID,以确保它们从1开始连续,在我的testing数据库上正常工作。
什么是SQL命令来改变列有一个身份属性?
您无法更改现有的身份列。
你有2个选项,
-
用标识创build一个新表并删除现有的表
-
用标识创build一个新的列并删除现有的列
方法1.( 新build表格 )您可以在这里保留新创build的标识列上的现有数据值。
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( 新列 )不能保留新创build的标识列上的现有数据值。标识列将保存编号的序列。
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论坛post:
如何将列更改为标识(1,1)
在SQL 2005及以上版本中,有一个技巧可以在不改变表的数据页的情况下解决这个问题。 这对于触摸每个数据页面的大型表格可能需要几分钟或几小时才是重要的。 即使身份列是主键,属于聚集索引或非聚集索引的一部分,或其他陷阱可能会使“简单的”添加/删除/重命名列“解决scheme发生变化,这个技巧也可以工作。
这里的技巧是:可以使用SQL Server的ALTER TABLE … SWITCH语句来更改表的模式而不更改数据,这意味着您可以使用具有相同表模式(但没有IDENTITY列)的IDENTITYreplace表。 同样的技巧可以将IDENTITY添加到现有的列。
通常情况下, ALTER TABLE … SWITCH用于有效地用新的空分区replace分区表中的完整分区。 但它也可以在非分区表中使用。
我已经使用这个技巧在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'; -- see same records SELECT * FROM Test;
这比其他答案中的解决scheme明显更复杂,但是如果你的桌子很大,这可能是一个真正的生活节省。 有一些注意事项:
- 您需要在执行切换之前先删除外键,然后再恢复。
- 与SCHEMABINDING函数,视图等相同
- 新表的索引需要完全匹配(相同的列,相同的顺序等)
- 新旧表格需要在同一个文件组上。
- 只适用于SQL Server 2005或更高版本
- 我以前认为这个技巧只适用于SQL Server的企业版或开发版(因为分区只在企业版和开发版中受支持),但是Mason G. Zhwiti在他的评论中表示,它也可以在SQL标准版中使用。 我认为这意味着企业或开发人员的限制不适用于ALTER TABLE … SWITCH。
TechNet上有一篇关于上述要求的文章 。
您不能将列更改为IDENTITY列。 你需要做的就是创build一个新的列,它被定义为一个IDENTITY从一开始,然后删除旧的列,并重新命名为旧名称。
ALTER TABLE (yourTable) ADD NewColumn INT IDENTITY(1,1) ALTER TABLE (yourTable) DROP COLUMN OldColumnName EXEC sp_rename 'yourTable.NewColumn', 'OldColumnName', 'COLUMN'
渣子
这里描述了很酷的解决scheme: SQL SERVER – 在列上添加或删除标识属性
简而言之,在SQLpipe理器中手动编辑你的表,切换标识,不保存更改,只显示将为更改而创build的脚本,将其复制并稍后使用。
这是非常节省时间的,因为它(脚本)包含所有与你所改变的表相关的外键,索引等。 手动写这个…上帝保佑。
通过devise,没有简单的方法来打开或closures现有列的身份识别function。 唯一干净的方法是创build一个新列,并将其设置为标识列或创build一个新表并迁移您的数据。
如果我们使用SQL Server Management Studio来删除“id”列上的标识值,则会创build一个新的临时表,将数据移动到临时表,旧表被删除,新表被重命名。
使用Management Studio进行更改,然后右键单击devise器并select“生成更改脚本”。
你会看到这是SQL服务器在后台执行的操作。
你不能这样做,你需要添加另一列,删除原来的列,并重新命名新的列或创build一个新的表,复制数据并删除旧表,然后将新表重命名为旧表
如果您使用SSMS并在devise器中将身份属性设置为ON,则这是SQL Server在幕后执行的操作。 所以,如果你有一个名为[用户]的表,这是如果你做UserID和身份会发生什么
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
说了有办法破解系统表来完成它通过设置按位值,但是不支持,我不会这样做
简单的解释
使用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)
设置主键的示例
新build的列名是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
现在我们添加一个主键和标识到表上的现有列。
没有一个,可悲的是; IDENTITY属性属于表而不是列。
更简单的方法是在GUI中执行,但如果这不是一个选项,则可以在复制数据,删除列,重新添加身份以及重新放回数据等方面进行很长时间。
在这里看到一个吹风帐户。
我是一个java开发人员,碰巧遇到一个没有DBA的团队,而且作为开发人员,我无法获得DBA的权利。 我的任务是在两个数据库之间移动整个模式,因此如果没有DBA,我必须通过运行脚本来完成,而不能在Sql Server 2008中使用GUI,因为我没有pipe理员权限。
一切都没有问题,但是,当在新的schema.table上运行存储过程时,我发现我丢失了表中的标识字段。 我再次检查了创build表的脚本,但是,当我运行脚本时,Sql Server没有得到它。 之后DBA告诉我他之前也看到过这个问题。
在任何情况下,对于Sql Server 2008,这些都是我为了解决这个问题而采取的步骤,所以我在这里发布这个信息,希望它能帮助某个人。 这就是我所做的,因为我在另一个表上使FK依赖关系变得更加困难:
我使用这个查询来validation身份确实缺失,并查看表上的依赖关系。
1.)在表格中查找统计信息:
exec sp_help 'dbo.table_name_old';
2.)创build一个重复的,相同的新表,除了在之前的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.)validation数据在那里。
SELECT * FROM dbo.table_name_new
6.)重新启用身份。
SET IDENTITY_INSERT ToyRecP.ToyAwards.lkpFile_New OFF
7.) 这是我发现获得所有FK关系的最好的脚本,以validation原始表引用哪个表作为依赖关系,而且我遇到了很多问题,所以它是一个守护者!
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(s):
ALTER TABLE [dbo].[table_name] DROP CONSTRAINT [Name_of_FK]
11.)放下原来的桌子:
DROP TABLE dbo.table_name_old;
)接下来的这些步骤依赖于您在第9步中在Sql Server 2008中创build的脚本。
– 在新表上添加PK。
– 在新表上添加FK。
– 将FK添加回依赖关系表。
14.)确认一切正确和完整。 我使用GUI来查看表格。
)将新表重命名为原始表名。
exec sp_RENAME '[Schema_Name.OldTableName]' , '[NewTableName]';
最后,一切正常!
接受的答案是错误的: 只要不包含任何空值,就可以将现有的列更改为标识。 更改后,身份种子将从最大(列)+1开始。
所以你首先需要做的是为所有的空值提供值。
正如我在正常情况下所理解的,我们正在创build一个具有身份属性的 主键的表
因此, 重命名或删除与主键 约束关联的列是不可能的,因为约束规则正在validation列结构。
为了达到这个目标,我们必须按以下方式处理一些步骤:
让我们假设TableName ='Employee'和ColumnName ='EmployeeId'
1.在“员工”表中添加新的“EmployeeId_new”列
ALTER TABLE Employee ADD EmployeeId_new INT IDENTITY(1,1)
-
现在从“Employee”表中删除“EmployeeId”列
ALTER TABLE雇员DROP COLUMN EmployeeId -
这将抛出错误,因为主键约束规则适用并validation列结构。
* ###' Msg 5074,Level 16,State 1,Line 1对象[PK_dbo.Employee]依赖于colmn [EmployeeId]。 ### -
所以我们必须首先从表'Employee'中删除主键约束,然后我们可以删除该列
ALTER TABLE雇员DROP约束[PK_dbo.Employee] -
现在我们可以像前一步那样从'Employee'表中删除'EmployeeId'列,在那里我们得到了错误
ALTER TABLE雇员DROP COLUMN EmployeeId -
现在将Column'EmployeeId'从表中移除因此,我们将用'EmployeeId'重新命名新添加的新列'EmployeeId_new'
sp_rename'Employee.EmployeeId','EmployeeId_new','COLUMN' -
要重新排列表格,就像我们必须为“EmployeeId”列添加主键约束,
ALTER TABLE雇员添加约束[PK_dbo.Employee]主键(EmployeeId)
8. 现在,具有“EmployeeId”的表“Employee”会随着退出主键约束而被修改
如果原始的海报实际上是想将现有的列设置为表的PRIMARY KEY
,而实际上并不需要该列是一个IDENTITY
列(两个不同的东西),那么这可以通过t-SQL完成:
ALTER TABLE [YourTableName] ADD CONSTRAINT [ColumnToSetAsPrimaryKey] PRIMARY KEY ([ColumnToSetAsPrimaryKey])
注意PRIMARY KEY
选项之后的列名称周围的圆括号。
尽pipe这篇文章比较陈旧,而且我正在做一个关于请求者需求的假设,但是我觉得这些额外的信息对于遇到这个线程的用户可能是有帮助的,因为我相信这个对话可能导致人们相信现有的列不能被设置为主键没有添加它作为一个新的列,这将是不正确的。
在SQL服务器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
从这里: 序列作为列的默认值
在对象资源pipe理器中右键单击表名称。 你会得到一些select。 点击“devise”。 此表格将打开一个新选项卡。 您可以在“列属性”中添加标识约束。
要修改列的标识属性,请执行以下操作:
- 在“服务器资源pipe理器”中,右键单击要修改标识属性的表,然后单击“打开表定义”。 表格在表devise器中打开。
- 清除要更改的列的允许空值checkbox。
- 在“列属性”选项卡中,展开“标识规范”属性。
- 单击Is Identity子元素的网格单元格,然后从下拉列表中selectYes。
- 在身份种子单元格中键入一个值。 该值将被分配给表中的第一行。 值1将被默认分配。
就是这样,它为我工作
我不相信你可以使用tsql将现有的列更改为标识列。 但是,您可以通过企业pipe理器devise视图来完成。
或者,您可以创build一个新行作为标识列,删除旧列,然后重新命名您的新列。
ALTER TABLE FooTable ADD BarColumn INT IDENTITY(1, 1) NOT NULL PRIMARY KEY CLUSTERED
基本上有四个合乎逻辑的步骤。
-
创build一个新的标识列。 打开这个新列的插入标识。
-
将数据从源列(您希望转换为Identity的列)插入到新列中。
-
closures新列的插入标识。
-
放下源列,并将新列重命名为源列的名称。
可能有一些更复杂的工作,如在多个服务器上工作等
请参阅下面的文章的步骤(使用ssms&T-sql)。 这些步骤适用于对T-SQL不太熟悉的初学者。
为没有标识集的主键= 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;
按照我目前的情况,我遵循这个方法。 我想在通过脚本插入数据后给主表赋予一个主表。
因为我想追加身份,所以它总是从1开始到我想要的logging数结束。
--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
这将创build与身份相同的主键列
我用这个链接: https : //blog.sqlauthority.com/2014/10/11/sql-server-add-auto-incremental-identity-column-to-table-after-creating-table/
将主键添加到现有表