我在另一个数据库中已经存在的数据库中创建了一个表。它最初是用旧的数据库数据填充的。表的 PK 必须接收那些记录上已经存在的值,所以它不能是自动增量的。
现在我需要新表将其 PK 作为自动增量。但是在 PK 已经存在并且有数据之后,我该怎么做呢?
我在另一个数据库中已经存在的数据库中创建了一个表。它最初是用旧的数据库数据填充的。表的 PK 必须接收那些记录上已经存在的值,所以它不能是自动增量的。
现在我需要新表将其 PK 作为自动增量。但是在 PK 已经存在并且有数据之后,我该怎么做呢?
我理解你的问题的方式是你有一个现有的表,其中有一列到目前为止已经填充了手动值,现在你想要 (1) 使这个列成为一个
IDENTITY
列,并且 (2) 确保IDENTITY
开始从现有行中的最新值。首先,一些测试数据可以使用:
目标是使表的主键列 ,
id
成为一个IDENTITY
从 21 开始的列,用于插入下一条记录。对于此示例,该列xyz
表示该表的所有其他列。在你做任何事情之前,请阅读这篇文章底部的警告。
首先,万一出现问题:
现在,让我们添加一个临时工作列,
id_temp
并将该列设置为现有id
列的值:接下来,我们需要删除现有
id
列(您不能只是“添加”IDENTITY
到现有列,您必须将列创建为IDENTITY
)。主键也必须去,因为列依赖它。...并再次添加该列,这次作为
IDENTITY
,以及主键:这就是有趣的地方。您可以
IDENTITY_INSERT
在表上启用,这意味着您可以IDENTITY
在插入新行时手动定义列的值(但不更新现有行)。使用该设置,
DELETE
表中的所有行,但您要删除的行都在OUTPUT
同一个表中 - 但列具有特定值id
(来自备份列)。一次,完成,
IDENTITY_INSERT
再次关闭。删除我们添加的临时列:
最后,为列重新设定种子
IDENTITY
,因此下一条记录id
将在列中现有的最高数字之后恢复id
:检查示例表,最高
id
数字为 20。添加另一行并检查其新行
IDENTITY
:在示例中,新行将具有
id=21
. 最后,如果您满意,请提交事务:重要的
这不是一个微不足道的操作,它带有很多你应该注意的风险。
在专用的测试环境中执行此操作。有备份。:)
我喜欢使用它,
BEGIN/COMMIT TRANSACTION
因为它可以防止其他进程在您更改表格时弄乱表格,并且如果出现问题,它可以让您回滚所有内容。但是,在您提交事务之前尝试访问您的表的任何其他进程最终都会等待。如果您有一张大桌子和/或您在生产环境中,这可能会非常糟糕。OUTPUT .. INTO
如果您的目标表具有外键约束或我不记得的许多其他功能中的任何一个,则将无法使用。您可以改为将数据卸载到临时表中,然后将其重新插入原始表中。您也许可以使用分区切换(即使您不使用分区)。一个接一个地运行这些语句,而不是作为批处理或在存储过程中。
尝试考虑可能取决于
id
您要删除和重新创建的列的其他事情。必须删除并重新创建任何索引(就像我们对主键所做的那样)。请记住为您需要预先重新创建的每个索引和约束编写脚本。禁用表上的任何
INSERT
和DELETE
触发器。如果重新创建表是一个选项:
如果您可以选择重新创建表,那么一切都会简单得多:
id
列为IDENTITY
,IDENTITY_INSERT ON
为餐桌设置,IDENTITY_INSERT OFF
, 和使用 UPDATE、DELETE 或 INSERT 移动数据可能会花费大量时间并使用数据和日志文件/磁盘上的资源 (IO)。在处理大表时,可以避免用可能的大量记录填充事务日志:使用分区切换,仅更改元数据。
不涉及数据移动,因此执行速度非常快(几乎是瞬时的)。
样品表
该问题未显示原始表 DDL。以下 DDL 将用作此答案中的示例:
使用此查询添加了从 0 到 15 的六个虚拟随机 ID:
示例数据
IdT
新表与
IDENTITY(0, 1)
唯一的问题
idT
是缺少IDENTITY(0, 1)
id 上的属性。一个具有类似结构的新表IDENTITY(0, 1)
被创建:除了
IDENTITY(0, 1)
,idT_Switch
与 相同idT
。外键
必须删除外键
idT
才能使用此技术。分区开关
idT
和idT_Switch
表具有兼容的结构。除了使用DELETE
,UPDATE
和INSERT
语句将行从自身移动idT
到自身idT_Switch
或在idT
自身之上,ALTER TABLE ... SWITCH
可以使用:PK_idT
(整个表)的单个“分区”被移动到PK_idT_Switch
(反之亦然)。idT
现在包含 0 行并idT_Switch
包含 6 行。您可以在此处找到源和目标兼容性要求的完整列表:
使用分区切换高效传输数据
请注意,这种使用
SWITCH
不需要企业版,因为没有显式分区。从 SQL Server 2005 开始,未分区表被认为是具有单个分区的表。代替
idT
idT
现在是空的和无用的,可以丢弃:idT_Switch
可以重命名并将替换旧idT
表:外键
外键可以再次添加到新
idT
表中。以前为了使表兼容切换而删除的任何其他内容idT
也需要重做。补种
此命令返回 0。表 idT 包含 6 行,MAX(id) = 15。可以使用DBCC CHECKIDENT (table_name) :
因为 15 比 0 大,所以它会自动重新播种而不寻找 MAX(id):
IDENT_CURRENT 现在返回15。
测试并添加数据
一个简单的
INSERT
声明:添加这一行:
该
id
列现在使用标识,新插入的值确实是 16 (15+1)。更多信息
这里有一个相关的问题和答案,其中包含有关该
SWITCH
技术的更多背景:为什么不支持删除列上的 Identity 属性
如果你想从一个新的身份值开始,你需要重新设定你的身份。查看文档
CHECKIDENT
启用和禁用 IDENTITY_INSERT
如果您的表是 TABLE_A 那么
如果您有像我这样的情况,那么您可以更轻松地完成此任务
第 1 步:获取一组好的数据,包括键字段值(在我的情况下,问题是主键无法与其他两个表正确同步)
第 2 步:使用 SSMS 功能创建您需要创建的表的删除和创建表。此外,请确保 NEW 表添加了主标识键
步骤 3:使用以下查询插入 PK 的相同增量
第 4 步:测试表是否相同
最后一步:请注意,插入语句没有使用 PK 字段....它仅用于对插入的数据进行正确排序:)