DEBUG.WIKI
京东云;让 AI 进入真实业务;零售 · 物流 · 客服 · 硬件;查看方案 →
ZgoCloud:多地区部署,按需选节点;日本、美国、香港、德国
Evoxt;最高 6.0GHz CPU;高频云服务器 · 免费每周备份;查看配置 →

SQL Server 报 IDENTITY_INSERT OFF:普通新增不要传自增 ID

SQL Server 自增列收到显式 ID,普通新增省略该列;历史数据迁移按同一会话使用 SET IDENTITY_INSERT,并核对约束、权限和恢复。

SQL Server 新增记录时提示 Cannot insert explicit value for identity column,并说明 IDENTITY_INSERT is set to OFF。如果表的 ID 列定义为 IDENTITY,而 INSERT 明确提供了 ID,先确认这次操作是普通新增,还是必须保留历史编号的迁移。

普通新增通常应从插入列清单中省略自增 ID,让数据库生成编号。只有明确需要保留编号时,才考虑在受控会话中开启 IDENTITY_INSERT。不要为了每次新增都能传一个 ID,就删除表的自增定义。

核对真正发送的列清单

查看当前数据库里的表定义,以及应用实际生成的 INSERT。对象模型中 ID 的默认值是零,不代表 ORM 没有把零写进 SQL;关键是这列是否出现在插入清单和绑定参数中。

下面假设 dbo.Tasks 已存在,ID 是自增列,Title 是普通业务字段。这是写入结构示例,应先在隔离测试表验证,不能直接写入业务表:

INSERT INTO dbo.Tasks (Title)
VALUES (N'demo task');

示例没有列出 ID,因此 SQL Server 负责生成它。若应用固定输出 (ID, Title) 并给 ID 绑定一个值,需要调整新增入口或 ORM 的生成值映射,不能只在应用对象里把编号改成另一个数字。

插入应明确列清单,不依赖表中列的顺序。若删除 ID 参数后其他字段仍错位,应同步修正列与参数的对应关系。编号存在间隔本身不表示失败,不能用“填满空号”作为普通新增的目标。

迁移保留编号时,使用会话范围的 SET

导入历史记录且编号参与业务关联时,先备份数据、核对主外键及重复编号,并确认执行账号拥有表或具有该表的 ALTER 权限。这是迁移权限,不应为普通应用新增长期扩大权限。

正确的配置语法是 SET IDENTITY_INSERT dbo.Tasks ON,不是 ALTER TABLE ... SET IDENTITY_INSERT。以下只展示开启、写入和关闭的对应关系;名称与编号都是示例,正式流程还必须包含事务与错误清理:

SET IDENTITY_INSERT dbo.Tasks ON;
INSERT INTO dbo.Tasks (ID, Title)
VALUES (123, N'imported task');
SET IDENTITY_INSERT dbo.Tasks OFF;

这三步必须作用于同一个会话。一个窗口打开设置,另一个窗口或连接池新借出的连接执行 INSERT,并不能继承它。同一会话同一时刻只能为一张表开启该选项,迁移多个表时应逐个完成并关闭。

开启选项不会跳过主键、外键或其他约束。插入重复编号仍会失败;失败路径也要保证关闭会话中的设置,不能让连接直接回池后继续承担无关业务。实际迁移宜使用明确拥有的专用连接,结束后关闭,避免把会话状态清理依赖于连接池的实现。

检查后续自动编号及已提交数据

在正向增长的 IDENTITY 列中,显式插入的值超过当前编号时,SQL Server 会把这个值作为新的当前编号。保留历史编号可能改变后续自动分配的起点,不能将它当成只影响这一行的开关;也不要照搬 PostgreSQL 的序列行为来判断 SQL Server。

变更前记录表定义、编号状态和迁移范围,在隔离副本检查普通新增与保留编号两条路径。正式迁移采用已审定的事务与恢复流程,避免错误后重跑已提交部分。

验收时,普通新增不再发送 ID,取得数据库生成的编号并能关联正确记录;迁移入口保存预期编号,所有约束成立,关闭选项后下一次普通新增符合预期。数据内容与业务关联也需要核对,不能只看报错消失。

回退应用插入代码或执行 SET ... OFF 不会删除已提交的导入记录,也不保证撤销编号状态变化。若迁移需要恢复,应按此前准备的备份与对账方案处理,不能为恢复号码直接重置整表编号。