向现有列添加标识
Posted
技术标签:
【中文标题】向现有列添加标识【英文标题】:Adding an identity to an existing column 【发布时间】:2010-11-06 03:49:39 【问题描述】:我需要将表的主键更改为标识列,并且表中已经有很多行。
我有一个脚本来清理 ID 以确保它们从 1 开始是连续的,在我的测试数据库上运行良好。
将列更改为具有标识属性的 SQL 命令是什么?
【问题讨论】:
【参考方案1】:您不能更改现有的身份列。
你有两个选择,
创建具有标识的新表并删除现有表
使用标识创建一个新列并删除现有列
方法1.(新建表)这里可以在新创建的标识列上保留现有的数据值。请注意,如果不满足“如果不存在”,您将丢失所有数据,因此请确保您也将条件放在 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 论坛帖子:
How to alter column to identity(1,1)
【讨论】:
如果表数据很小,这个选项很好用。如果表很大,我更喜欢另一个选项:使用 ALTER TABLE ... SWITCH 将表模式替换为具有 IDENTITY 列但其他模式相同的另一个版本。 ALTER TABLE.... SWITCH 方法的优点是它可以快速完成(对于十亿行表来说,不到 5 秒),因为不需要复制或更改表数据。但是有一些警告和限制。有关详细信息,请参阅下面的答案。 @Justin Grat:一个非常有趣的替代方案,我没有考虑过!这样做的原因是因为 IDENTITY 是列属性而不是数据类型,因此 SWITCH 方法验证两个表(旧表和新表)之间的模式是可识别的,而不管 IDENTITY 差异如何。感谢分享! 如果数据不多,那么“建表”可以通过SSMS生成脚本来实现。右键单击表 > 将表作为 > 创建表到 >(新查询编辑器?)。然后删除它,在该脚本中,您可以添加带有主键列的IDENTITY(1, 1)
部分
也可以使用 SSMS 来强制执行此操作。转到工具>选项>设计器>取消选中“防止保存需要重新创建表的更改”。顺便说一句,对于相当大的表不建议这样做。
在 PostgreSQL 中,您可以使用以下命令将标识添加到现有整数列:alter table table_name alter column column_name add generated always as identity(以 number 重新启动);【参考方案2】:
在 SQL 2005 及更高版本中,有一个技巧可以在不更改表的数据页的情况下解决此问题。这对于触摸每个数据页可能需要几分钟或几小时的大型表来说很重要。即使标识列是主键,是聚集或非聚集索引的一部分,或者其他可能导致更简单的“添加/删除/重命名列”解决方案的陷阱,该技巧也有效。
诀窍在于:您可以使用 SQL Server 的 ALTER TABLE...SWITCH 语句来更改表的架构而不更改数据,这意味着您可以用相同的表架构替换带有 IDENTITY 的表,但没有 IDENTITY 列。相同的技巧可以将 IDENTITY 添加到现有列。
通常,ALTER TABLE...SWITCH 用于有效地将分区表中的完整分区替换为新的空分区。但它也可以用于非分区表。
我使用这个技巧在 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;
这显然比其他答案中的解决方案涉及更多,但如果您的表很大,这可能是一个真正的救生员。有一些注意事项:
据我所知,身份是唯一可以使用此方法更改表列的内容。不允许添加/删除列、更改可空性等。 您需要在切换之前删除外键并在之后恢复它们。 WITH SCHEMABINDING 函数、视图等也是如此。 新表的索引需要完全匹配(相同的列、相同的顺序等) 新旧表需要在同一个文件组中。 仅适用于 SQL Server 2005 或更高版本 我以前认为这个技巧只适用于 SQL Server 的 Enterprise 或 Developer 版本(因为分区仅在 Enterprise 和 Developer 版本中受支持),但 Mason G. Zhwiti 在下面的评论中说它也适用于 SQL Standard版也。我认为这意味着对 Enterprise 或 Developer 的限制不适用于 ALTER TABLE...SWITCH。有一个很好的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
很好。
【讨论】:
如果我没记错的话,我是从这篇文章中了解到的:sqlservercentral.com/articles/T-SQL/61979 仅供参考,这似乎也适用于 SQL 2008 R2 的标准版本。也许他们启用了此功能,就像他们现在启用了启用备份压缩的功能一样。 @jbatista - OP 的问题表明他已经在表上有一个主键并且已经可以确保正确的值,但他只是想将其更改为 IDENTITY 列。我上面的回答集中在那个狭窄的用例上:如何在不实际更改任何数据的情况下将 IDENTITY 添加到列中。我在上面记录的方法对于大型表来说是一个巨大的节省时间。如果您需要更改数据,则需要使用其他解决方案。 这里还有一个值得一提的警告。尽管新表会愉快地从旧表接收数据,并且所有新行都将按照标识模式插入,但它们将从 1 开始,如果所述列是主键,则可能会中断。考虑在切换后立即运行DBCC CHECKIDENT('<newTableName>')
。请参阅msdn.microsoft.com/en-us/library/ms176057.aspx 了解更多信息。
这是一个很好的答案!另请注意,列的可空性必须相同。因此,如果您需要更改列的可空性,则必须在稍后的步骤中进行。 PK 约束也是如此。我还更改了表创建中的标识值以匹配当前最大值:IDENTITY (maxID+1, 1)【参考方案3】:
您不能将列更改为 IDENTITY 列。您需要做的是创建一个从一开始就定义为 IDENTITY 的新列,然后删除旧列,并将新列重命名为旧名称。
ALTER TABLE (yourTable) ADD NewColumn INT IDENTITY(1,1)
ALTER TABLE (yourTable) DROP COLUMN OldColumnName
EXEC sp_rename 'yourTable.NewColumn', 'OldColumnName', 'COLUMN'
马克
【讨论】:
参数 \@objname 不明确或声明的 \@objtype (COLUMN) 错误。 @JennyO'Reilly:把它放到一个单独的问题中,然后向我们展示你正在使用的完整命令! 失败的是 sp_rename 过程。我通过搜索错误文本找到了关于 *** 的解决方案。尽管我的表格名称中没有任何特殊字符,但这似乎是一些带有括号的严格语法规则。 或者它可能是:'ALTER TABLE (yourTable) DROP COLUMN OldColumnName' 和 'ALTER TABLE (yourTable) ADD OldColumnName INT IDENTITY(1,1)',为什么要重命名:p 马克,我在一张巨大的桌子(约 3 亿行)上尝试了这个确切的命令,但我在约 10 分钟后停止了该过程【参考方案4】:这里描述了一个很酷的解决方案: SQL SERVER – Add or Remove Identity Property on Column
简而言之,在 SQL 管理器中手动编辑您的表,切换身份,不要保存更改,只需显示将为更改创建的脚本,复制它并稍后使用它。
它可以节省大量时间,因为它(脚本)包含与您更改的表相关的所有外键、索引等。手动编写...上帝保佑。
【讨论】:
这是我使用的解决方案——SSMS 生成 T-SQL 以进行更改...它通过创建一个具有相同模式设计的新临时表来实现,然后将所有行复制到它,删除原件,并重命名。完全运行可能需要一点时间,但运行良好。 我不认为 Pinal Dave 实际上是在说您需要运行您生成的脚本,它只是为了展示通过 UI 进行更改对您有什么作用... SSMS 中的这个脚本工具(关于更改表的定义)实际上是记录分区表时唯一正确的工具。最合适的位置'task'->'script table' 总是忘记给分区函数写脚本! 可能对某人有帮助。更改后获取更改脚本。在 SSMS 上右键单击设计模式下的表,然后选择“生成更改脚本”选项并将脚本保存在本地驱动器中【参考方案5】:考虑使用SEQUENCE instead of IDENTITY。
IN 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
从这里:Sequence as default value for a column
【讨论】:
【参考方案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
现在我们向表中的现有列添加主键和标识。
【讨论】:
【参考方案7】:我是一名 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]';
终于,一切顺利!
【讨论】:
【参考方案8】:你不能那样做,你需要添加另一列,删除原始列并重命名新列,或者创建一个新表,复制数据并删除旧表,然后重命名新表到旧桌子上
如果您使用 SSMS 并在设计器中将标识属性设置为 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】:据我了解,在正常情况下,我们正在创建一个具有 主键 的表,该表具有 Identity 属性 所以 Rename 或 Delete 与 Primary Key constraint 关联的列将是不可能的,因为约束规则正在验证列结构。 为了实现这一点,我们必须按以下方式处理一些步骤: 让我们假设 TableName = 'Employee' 和 ColumnName = 'EmployeeId' 1.在'Employee'表中添加新列'EmployeeId_new' ALTER TABLE Employee ADD EmployeeId_new INT IDENTITY(1,1)
现在从 'Employee' 表中删除列 'EmployeeId' ALTER TABLE Employee DROP COLUMN EmployeeId
这将引发错误,因为主键约束规则适用并且正在验证列结构。 *### 'Msg 5074,Level 16,State 1,Line 1 对象 [PK_dbo.Employee] 依赖于 colmn [EmployeeId]。'###
所以我们必须先从表 'Employee' 中删除主键约束,然后才能删除列 ALTER TABLE Employee DROP 约束 [PK_dbo.Employee]
现在我们可以从 'Employee' 表中删除列 'EmployeeId',就像在上一步出现错误时所做的那样 ALTER TABLE Employee DROP COLUMN EmployeeId
现在从表中删除列“EmployeeId” 所以我们将新添加的新列 'EmployeeId_new' 重命名为 'EmployeeId' sp_rename 'Employee.EmployeeId', 'EmployeeId_new', 'COLUMN'
要以与以前相同的形式重新排列表格,我们必须为“EmployeeId”列添加主键约束 ALTER TABLE Employee 添加约束 [PK_dbo.Employee] 主键 (EmployeeId)
8. 现在,带有 'EmployeeId' 的表 'Employee' 已针对身份规则以及现有的主键约束进行了修改
【讨论】:
【参考方案10】:按照设计,没有简单的方法可以打开或关闭现有列的标识功能。唯一干净的方法是创建一个新列并使其成为标识列,或者创建一个新表并迁移您的数据。
如果我们使用 SQL Server Management Studio 去除列“id”上的标识值,则会创建一个新的临时表,将数据移动到临时表,删除旧表并重命名新表.
使用 Management Studio 进行更改,然后在设计器中单击鼠标右键并选择“生成更改脚本”。
您会看到这就是 SQL Server 在后台所做的。
【讨论】:
【参考方案11】:如果您碰巧使用的是 Visual Studio 2017+
-
在服务器对象资源管理器中右键单击您的表并选择“查看代码”
将修饰符“IDENTITY”添加到您的列中
更新
这一切都会为您完成。
【讨论】:
是的!谢谢你提出这个建议!我的 Windows 7 机器上没有 SSMS 版本,它允许我对生产服务器上的表进行设计更改,因为它是 2017 年,我的 SSMS 是 2014 年,而 2017 年 SSMS 需要 Windows 10。你让我很开心。进入 VS 2017 > 服务器资源管理器 > 与生产 SQL Server 建立新连接 > 右键单击表 > “打开表定义” > Wala! 实际上,我发现您可以右键单击该字段并选择“属性”并在那里选择“是”或“否”。【参考方案12】:遗憾的是,没有一个; IDENTITY 属性属于表而不是列。
更简单的方法是在 GUI 中执行此操作,但如果这不是一个选项,您可以复制数据、删除列、重新添加标识以及放置数据返回。
请参阅here 了解详细信息。
【讨论】:
【参考方案13】:如果原始发布者实际上想要将现有列设置为表的 PRIMARY KEY
并且实际上不需要将列设置为 IDENTITY
列(两个不同的事情),那么这可以通过 t -SQL 与:
ALTER TABLE [YourTableName]
ADD CONSTRAINT [ColumnToSetAsPrimaryKey] PRIMARY KEY ([ColumnToSetAsPrimaryKey])
注意PRIMARY KEY
选项后面的列名周围的括号。
虽然这篇文章已经过时,并且我对请求者的需求做出了假设,但我认为这些额外信息可能对遇到此线程的用户有所帮助,因为我相信对话可能会让人们相信无法设置现有列成为主键而不先将其添加为新列,这是不正确的。
【讨论】:
【参考方案14】:在对象资源管理器中右键单击表名。你会得到一些选择。点击“设计”。将为该表打开一个新选项卡。您可以在“列属性”中添加身份约束。
【讨论】:
如果你这样做......表将被删除并重新创建。【参考方案15】:修改列的标识属性:
在服务器资源管理器中,右键单击要修改身份属性的表,然后单击打开表定义。 表格在表格设计器中打开。 清除要更改的列的允许空值复选框。 在“列属性”选项卡中,展开“身份规范”属性。 单击 Is Identity 子属性的网格单元格,然后从下拉列表中选择 Yes。 在标识种子单元格中键入一个值。该值将分配给表中的第一行。默认情况下将分配值 1。就是这样,它对我有用
【讨论】:
【参考方案16】:我不相信您可以使用 tsql 将现有列更改为标识列。但是,您可以通过 Enterprise Manager 设计视图来完成。
或者,您可以创建一个新行作为标识列,删除旧列,然后重命名您的新列。
ALTER TABLE FooTable
ADD BarColumn INT IDENTITY(1, 1)
NOT NULL
PRIMARY KEY CLUSTERED
【讨论】:
请记住,如果您通过 SSMS/企业管理器执行此操作 - 您将创建一个新表、复制数据、删除旧表并重命名新表。当您有大桌子时,这可能会非常昂贵......【参考方案17】:根据我目前的情况,我遵循这种方法。通过脚本插入数据后,我想为主表提供标识。
因为我想附加身份,所以它总是从 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
这将创建与身份相同的主键列
我使用了这个链接:https://blog.sqlauthority.com/2014/10/11/sql-server-add-auto-incremental-identity-column-to-table-after-creating-table/
Add primary key to existing table
【讨论】:
【参考方案18】:基本上有四个逻辑步骤。
创建一个新的身份列。为这个新列启用插入标识。
将源列(您希望转换为标识的列)中的数据插入到这个新列中。
关闭新列的插入标识。
删除您的源列并将新列重命名为源列的名称。
可能会有一些更复杂的问题,例如跨多个服务器工作等。
步骤请参考以下文章(使用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
【讨论】:
【参考方案19】:为所有没有身份集的主键 = 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;
【讨论】:
以上是关于向现有列添加标识的主要内容,如果未能解决你的问题,请参考以下文章