结论是必须先执行set identity_insert 表名 on,再显式插入标识列值,最后执行set identity_insert 表名 off;该设置为会话级、单表级,且insert语句中只要列出标识列名即触发检查,默认identity_insert为off导致报错msg 544。

直接说结论:报错 Msg 544, Level 16, State 1 ——“当 IDENTITY_INSERT 设置为 OFF 时,无法为表 'xxx' 中的标识列插入显式值”,本质是你在 INSERT 语句里写了标识列(比如 ID),但没开 SET IDENTITY_INSERT 表名 ON,而且这个开关不能跨会话、不能对多个表同时生效。
INSERT 语句里写了标识列名就一定会报错
只要你的 INSERT 显式列出了标识列(例如 INSERT INTO Orders (ID, OrderDate) VALUES (1001, '2026-08-11')),SQL Server 就会检查当前会话的 IDENTITY_INSERT 状态。默认是 OFF,一碰就炸。
- 哪怕你插的是
NULL或DEFAULT,只要列名出现在括号里,就算“显式指定” - SSMS 生成的脚本有时会自动带标识列,IDEA/DataGrip 更常见——它们不帮你加
SET IDENTITY_INSERT -
SELECT ... INTO或INSERT ... SELECT同样受限制,不是只有单行VALUES才触发
SET IDENTITY_INSERT 必须配对使用且作用域极窄
SET IDENTITY_INSERT 不是全局配置,而是会话级、单表级的临时开关。漏掉任一环节都会出问题。
- 必须先执行
SET IDENTITY_INSERT 表名 ON,再执行INSERT,最后执行SET IDENTITY_INSERT 表名 OFF - 同一个会话里,不能对两个表同时开启——比如开了
Orders,再开Customers会报错 - 如果事务中途失败,
OFF没执行到,下次同会话再操作该表仍可能成功(但极不推荐依赖这种状态) - 存储过程里用它,要注意调用者会话是否已开启,不要假设上下文一致
更安全的做法:根本别碰标识列
90% 的业务插入不需要手动指定标识值。绕过报错最稳的方式,是让 SQL Server 自己生成。
- 写 INSERT 时**完全省略标识列**:
INSERT INTO Orders (OrderDate, CustomerID) VALUES ('2026-08-11', 123) - ORM 如 MyBatis 或 JPA,确保实体类中标识字段没被赋值,或明确标注
@TableId(type = IdType.AUTO) - 如果用 JDBC,预编译语句的占位符
?列表里**不要包含标识列**,对应PreparedStatement的setXxx()调用也跳过它 - 导数据时若需保留原 ID(如迁移、修复),才考虑
IDENTITY_INSERT;日常增删改查一律回避
真正容易被忽略的点是:工具自动生成的 SQL 往往“看起来合理”,但列名全写、值全填,却忘了加开关——这不是语法错,是机制错。修法不在补 ON/OFF,而在确认你**为什么非得插那个标识值**。










