我需要将表的主键更改为标识列,并且表中已有许多行。
我有一个脚本来清理ID以确保它们从1开始顺序启动,在我的测试数据库上运行正常。
将列更改为具有标识属性的SQL命令是什么?
答案 0 :(得分:432)
您无法更改现有列的标识。
您有2个选项,
使用identity&创建一个新表删除现有表
使用identity&创建一个新列删除现有列
方法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'
方法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 :(得分:182)
在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;
这显然比其他答案中的解决方案更复杂,但如果你的桌子很大,这可以真正节省生命。有一些警告:
有一个很好的article on TechNet详细说明了上述要求。
更新 - Eric Wu下面有一条评论,其中添加了有关此解决方案的重要信息。在此复制以确保它得到更多关注:
这里有另一个值得一提的警告。想到了 新表将很乐意从旧表和所有表中接收数据 新的行将按照身份模式插入,他们会 如果所述列是主键,则从1开始并可能中断。 考虑在之后立即运行
DBCC CHECKIDENT('<newTableName>')
交换。有关详情,请参阅msdn.microsoft.com/en-us/library/ms176057.aspx 资讯
如果使用新行主动扩展表(意味着在添加IDENTITY和添加新行之间没有太多停机时间,那么您需要手动设置标识种子而不是DBCC CHECKIDENT
新表模式中的值大于表中最大的现有ID,例如IDENTITY (2435457, 1)
。您可以在事务中包含ALTER TABLE...SWITCH
和DBCC CHECKIDENT
(或者不 - 没有测试过这个)但似乎手动设置种子值会更容易和更安全。
显然,如果没有新的行添加到表中(或者它们只是偶尔添加,就像每日ETL过程一样),那么这种竞争条件就不会发生,所以DBCC CHECKIDENT
没问题。
答案 2 :(得分:64)
您无法将列更改为IDENTITY列。您需要做的是创建一个新列,该列从一开始就定义为IDENTITY,然后删除旧列,并将新列重命名为旧名称。
ALTER TABLE (yourTable) ADD NewColumn INT IDENTITY(1,1)
ALTER TABLE (yourTable) DROP COLUMN OldColumnName
EXEC sp_rename 'yourTable.NewColumn', 'OldColumnName', 'COLUMN'
马克
答案 3 :(得分:14)
这里描述了很酷的解决方案: SQL SERVER – Add or Remove Identity Property on Column
简而言之,在SQL Manager中手动编辑表,切换标识,不要保存更改,只显示将为更改创建的脚本,复制并稍后使用。
节省大量时间,因为它(脚本)包含与您更改的表相关的所有外键,索引等。手动写这个......上帝保佑。
答案 4 :(得分:6)
考虑使用SEQUENCE instead of 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
答案 5 :(得分:6)
简单说明
使用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
Drop重命名列的示例
ALTER TABLE Users DROP COLUMN OldUserID
现在我们将主键和标识添加到表中的现有列。
答案 6 :(得分:5)
我是一名java开发人员,碰巧遇到没有DBA的团队,而且作为开发人员,我无法获得DBA权限。我的任务是在两个数据库之间移动整个模式,所以没有DBA,我必须通过运行脚本来完成它,而不能在SQL Server 2008中使用GUI,因为我没有管理员权限。
一切都没有问题,但是,当在新的schema.table上运行存储过程时,我发现我丢失了表中的标识字段。我仔细检查了创建表的脚本,但是,当我运行脚本时,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关系以验证原始表引用哪些表作为依赖关系的最佳脚本 而且我遇到了很多,所以它是守门员!
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。)接下来的步骤依赖于您在步骤9中在SQL Server 2008中创建的脚本。
- 将PK添加到新表中。
- 将FK添加到新表中。
- 将FK添加回依赖关系表。
14。)确认一切正确和完整。我使用GUI来查看表格。
15。)将新表重命名为原始表名。
exec sp_RENAME '[Schema_Name.OldTableName]' , '[NewTableName]';
最后,一切正常!
答案 7 :(得分:3)
按照设计,没有简单的方法可以打开或关闭现有列的标识功能。唯一干净的方法是创建一个新列并使其成为标识列或创建新表并迁移数据。
如果我们使用SQL Server Management Studio来删除列“id”上的标识值,则会创建一个新的临时表,将数据移动到临时表,删除旧表并重命名新表
使用Management Studio进行更改,然后右键单击设计器并选择“生成更改脚本”。
您将看到这是SQL服务器在后台执行的操作。
答案 8 :(得分:3)
你不能这样做,你需要添加另一列,删除原始列并重命名新列或者创建一个新表,复制数据并删除旧表,然后重命名新表到旧桌子
如果你使用SSMS并在设计器中将identity属性设置为ON,这就是SQL Server在幕后所做的事情。因此,如果您有一个名为[user]的表,那么如果您创建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
已经说过有一种方法可以通过设置按位值来破解系统表来实现它,但是不支持它,我不会这样做
答案 9 :(得分:3)
正如我在正常情况下所理解的那样,我们正在创建一个包含主键的表格,该表格具有标识属性
因此,重命名或删除与主键 约束相关联的列将无法实现,因为约束规则正在验证列结构。
要实现这一点,我们必须以下列方式处理一些步骤:
我们假设 TableName =&#39;员工&#39; 和 ColumnName =&#39; EmployeeId&#39;
1.添加新列&#39; EmployeeId_new&#39;在&#39;员工&#39;表
ALTER TABLE员工ADD EmployeeId_new INT IDENTITY(1,1)
现在删除列&#39; EmployeeId&#39;来自&#39;员工&#39;表
ALTER TABLE员工DROP COLUMN EmployeeId
由于主键约束规则适用并验证列结构,因此会抛出错误。
* ###
&#39; 消息5074,级别16,状态1,行1对象[PK_dbo.Employee]依赖于colmn [EmployeeId]。&#39; ## #
因此,我们必须先从表格&#39;员工&#39;中删除主键约束。然后我们可以删除该列
ALTER TABLE员工DROP约束[PK_dbo.Employee]
现在我们可以删除列&#39; EmployeeId&#39;来自&#39;员工&#39;表格与上一步中我们收到错误的表格一样
ALTER TABLE员工DROP COLUMN EmployeeId
现在专栏&#39; EmployeeId&#39;从表中删除
因此,我们将重新命名新添加的新列&#39; EmployeeId_new&#39;与&#39; EmployeeId&#39;
sp_rename&#39; Employee.EmployeeId&#39;,&#39; EmployeeId_new&#39;,&#39; COLUMN&#39;
要以与原来相同的形式重新排列表格,我们必须为列&#39; EmployeeId&#39;添加主键约束。
ALTER TABLE员工添加约束[PK_dbo.Employee]主键(EmployeeId)
8。 现在表&#39;员工&#39;与&#39; EmployeeId&#39;已针对身份规则以及现有主键约束
进行了修改答案 10 :(得分:2)
如果您恰好正在使用Visual Studio 2017 +
这将为您完成所有工作。
答案 11 :(得分:2)
修改列的标识属性:
那是它,它对我有用
答案 12 :(得分:2)
右键单击对象资源管理器中的表名称。你会得到一些选择。点击&#39;设计&#39;。将为此表打开一个新选项卡。您可以在&#39;列属性&#39;。
中添加标识约束答案 13 :(得分:2)
更简单的方法是在GUI中执行此操作,但如果这不是一个选项,您可以大大复制数据,删除列,重新添加标识,并放置数据回来。
请参阅here了解详细信息。
答案 14 :(得分:1)
根据我目前的情况,我遵循这种方法。我希望在通过脚本插入数据后为主表提供标识。
因为我想附加身份,所以它总是从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
这将创建具有标识
的相同主键列答案 15 :(得分:1)
如果原始海报实际上想要将现有列设置为表的PRIMARY KEY
,并且实际上不需要该列为IDENTITY
列(两个不同的东西),那么这可以通过t-SQL完成:
ALTER TABLE [YourTableName]
ADD CONSTRAINT [ColumnToSetAsPrimaryKey] PRIMARY KEY ([ColumnToSetAsPrimaryKey])
请注意PRIMARY KEY
选项后列名称周围的括号。
虽然这篇文章很老,而且我对请求者的需求做了一个假设,但我觉得这些额外的信息可能会对遇到这个帖子的用户有所帮助,因为我相信这个对话可能会导致人们相信现有的列无法设置成为主键而不首先将其作为新列添加,这将是不正确的。
答案 16 :(得分:0)
为主键= 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;
答案 17 :(得分:0)
基本上有四个逻辑步骤。
创建新的标识列。为此新列启用“插入标识”。
将源列(您希望转换为Identity的列)中的数据插入此新列。
关闭新列的插入标识。
删除您的源列&amp;将新列重命名为源列的名称。
可能存在一些更复杂的问题,例如跨多个服务器等工作。
请参阅以下文章了解步骤(使用ssms&amp; T-sql)。这些步骤适用于对T-SQL较少掌握的初学者。
答案 18 :(得分:0)
我不相信您可以使用tsql将现有列更改为标识列。但是,您可以通过企业管理器设计视图执行此操作。
或者,您可以创建一个新行作为标识列,删除旧列,然后重命名新列。
ALTER TABLE FooTable
ADD BarColumn INT IDENTITY(1, 1)
NOT NULL
PRIMARY KEY CLUSTERED