必须先执行set identity_insert 表名 on,再显式列出标识列插入数据,最后执行set identity_insert 表名 off;该设置为会话级、单表级,且insert必须显式指定列名,否则报错msg 544。

SQL Server 中如何对 IDENTITY 列显式插入值
默认情况下,SQL Server 禁止向 IDENTITY 列插入显式值,否则会报错:Cannot insert explicit value for identity column in table 'xxx' when IDENTITY_INSERT is set to OFF。要绕过这个限制,必须临时启用 IDENTITY_INSERT,且仅对单个表生效、每次只能开启一个表。
-
SET IDENTITY_INSERT 表名 ON必须在INSERT语句前执行,且用户需对该表有ALTER权限 - 开启后,
INSERT必须显式列出所有列(包括IDENTITY列),不能用INSERT INTO tbl VALUES (...)省略列名 - 插入完成后,应立即执行
SET IDENTITY_INSERT 表名 OFF;若未关闭,后续插入非显式值会失败(因为自增种子不会自动跳过已插入的大值) - 同一会话中不能对多个表同时开启
IDENTITY_INSERT,否则报错:IDENTITY_INSERT is already ON for table 'xxx'
PostgreSQL 中对应操作:使用 GENERATED ALWAYS AS IDENTITY 的处理方式
PostgreSQL 10+ 引入了 GENERATED ALWAYS AS IDENTITY,其行为比 SQL Server 更严格——默认完全禁止显式插入。若真需要插入指定值,有两种路径:
- 改用
GENERATED BY DEFAULT AS IDENTITY(建表时定义),此时插入时可显式提供值,不提供则由序列生成 - 若已是
GENERATED ALWAYS,需先ALTER TABLE tbl ALTER COLUMN id SET GENERATED BY DEFAULT,插入后再改回去(生产环境慎用) - 更安全的做法是:临时
ALTER TABLE tbl ALTER COLUMN id DROP IDENTITY,插入完再ADD GENERATED ALWAYS AS IDENTITY,但会丢失序列关联,需手动重置序列值(SELECT setval('seq_name', max_id))
MySQL / MariaDB:AUTO_INCREMENT 列能否显式插入
MySQL 对 AUTO_INCREMENT 列的限制宽松得多:只要插入的值不重复且大于当前最大值,就允许显式插入,无需任何开关。
- 例如:
INSERT INTO users (id, name) VALUES (999, 'test')可直接执行,前提是id是AUTO_INCREMENT主键且 999 未被占用 - 但若插入重复值或小于等于当前最大值,会报错:
Duplicate entry 'X' for key 'PRIMARY'或触发唯一约束冲突 - 注意:显式插入不会影响后续自增值——除非插入值 > 当前
AUTO_INCREMENT值,此时 MySQL 会自动将该值设为新的自增起点 - 可通过
SHOW CREATE TABLE tbl查看当前AUTO_INCREMENT值,必要时用ALTER TABLE tbl AUTO_INCREMENT = N手动调整
容易忽略的关键细节
跨数据库迁移或编写通用脚本时,最常踩的坑不是语法写错,而是对“自增列是否接受显式值”缺乏上下文判断:
- SQL Server 的
IDENTITY_INSERT是会话级、表级、临时性的,关掉即失效,但忘记关会导致后续常规插入失败 - PostgreSQL 的
GENERATED ALWAYS是 DDL 级强制策略,不是运行时开关,强行绕过会破坏设计契约 - MySQL 虽灵活,但显式插入大 ID 后可能造成自增间隙,影响分库分表时的 ID 连续性预期
- 所有场景下,显式插入前都应确认目标值未被占用,否则主键冲突无法回退










