SET IDENTITY_INSERT 是 SQL Server 表级、会话级、一次性生效的开关,用于允许向 IDENTITY 列显式插入值;必须先执行 SET IDENTITY_INSERT 表名 ON,INSERT 时显式指定列名,完成后应立即执行 OFF。

SET IDENTITY_INSERT 为什么必须显式开启
SQL Server 不允许直接向 IDENTITY 列插入显式值,这是底层保护机制——防止破坏自增序列的唯一性和连续性。强行绕过会触发错误:Cannot insert explicit value for identity column in table 'xxx' when IDENTITY_INSERT is set to OFF。所以必须先用 SET IDENTITY_INSERT 打开开关,且该设置是**表级、会话级、一次性生效**的,不能跨表或跨连接复用。
-
SET IDENTITY_INSERT 表名 ON必须在INSERT语句前执行,且同一时间只能对一张表启用 - 启用后必须显式列出列名(不能用
INSERT INTO 表 VALUES (...)省略列),否则报错 - 插入完成后建议立即执行
SET IDENTITY_INSERT 表名 OFF,避免后续误操作 - 即使只插一条数据,也必须成对使用 ON/OFF —— 不关掉不会自动恢复,下次插入普通数据会失败
INSERT 时列名和值必须严格对应
开启 IDENTITY_INSERT 后,INSERT 语句不能再省略列名。因为 SQL Server 需要明确知道你把哪个值塞进 IDENTITY 列,否则无法校验合法性。
- 正确写法:
INSERT INTO users (id, name, email) VALUES (100, 'Alice', 'a@b.com') - 错误写法:
INSERT INTO users VALUES (100, 'Alice', 'a@b.com')→ 报错:Column name or number of supplied values does not match table definition - 如果表有 5 列,其中第 2 列是
IDENTITY,那你在VALUES中填的第二个值,就会被当作要写入该列的显式值
插入后自增种子不会自动更新
手动插入一个大于当前最大值的 IDENTITY 值(比如当前最大是 50,你插了 100),SQL Server **不会**自动把 IDENTITY 种子调到 101。下次不指定值的 INSERT 仍按原种子 + 步长生成,很可能导致主键冲突。
- 查当前种子:
DBCC CHECKIDENT ('users', NORESEED) - 若需同步,得手动重置:
DBCC CHECKIDENT ('users', RESEED, 100)(下一次自增从 101 开始) - 重置操作本身不检查表中是否已存在该值,务必确认不会重复
- 生产环境慎用
RESEED,尤其在高并发写入场景下,可能引发不可预知的冲突
其他数据库不支持这种语法
SET IDENTITY_INSERT 是 SQL Server 特有语法。PostgreSQL 用 serial 或 IDENTITY 列时,需用 OVERRIDING SYSTEM VALUE;MySQL 的 AUTO_INCREMENT 列允许直接插入值(只要没设 NO_AUTO_VALUE_ON_ZERO),但不推荐;Oracle 的 IDENTITY 列默认禁止显式插入,需改用 GENERATED ALWAYS AS IDENTITY OVERRIDING SYSTEM VALUE。
别在迁移脚本里硬套 SQL Server 写法,不同数据库对“强制写自增列”的约束逻辑和语法差异很大,最容易出问题的地方就在这里。

















