必须先set identity_insert表名on,再显式指定所有列名insert,最后set off;同一会话仅能对一张表启用,需alter权限,且不跨连接生效。

SQL Server中开启IDENTITY_INSERT的正确姿势
想在 INSERT 时显式插入值到标识列(identity column),必须先用 SET IDENTITY_INSERT [table_name] ON 打开开关——但这个操作有严格限制:同一时间每个数据库会话只能对一张表启用,且执行者必须是该表的所有者或具有 ALTER 权限。
-
IDENTITY_INSERT是会话级设置,不是事务级,也不跨连接生效 - 开启后必须显式指定列名列表(不能用
INSERT INTO tbl VALUES (...)),否则报错Msg 8101 - 关闭前不能对同一表执行第二次
SET IDENTITY_INSERT ... ON,否则报错Msg 8107 - 即使只插一条数据,也必须先
ON、再INSERT、最后OFF,漏掉OFF可能导致后续其他脚本失败
INSERT语句必须显式列出所有列(含identity列)
开启 IDENTITY_INSERT 后,INSERT 语句不能再省略列名。系统会校验你提供的值是否与标识列定义兼容(比如不能插负数进 INT IDENTITY(1,1),除非定义允许)。
- 错误写法:
SET IDENTITY_INSERT users ON; INSERT INTO users VALUES (100, 'alice', 'a@b.com');→ 报错Msg 8101 - 正确写法:
SET IDENTITY_INSERT users ON; INSERT INTO users (id, name, email) VALUES (100, 'alice', 'a@b.com'); - 如果表有10列,你就得写全10个列名——哪怕只打算覆盖其中3个,其余7个也得填
DEFAULT或具体值(取决于是否允许 NULL)
为什么不能在同一个批次里重复开关或混用多表
SQL Server 强制约束 IDENTITY_INSERT 的独占性,本质是为了避免并发插入时 identity 值错乱或跳号不可控。一旦某个会话对 orders 表启用了它,当前连接就不能再对 customers 表执行 SET IDENTITY_INSERT customers ON,直到先把 orders 关掉。
- 典型报错:
Msg 8107, Level 16, State 1: Cannot set IDENTITY_INSERT to ON for table 'customers' because it is already set to ON for table 'orders'. - 解决办法只有两个:要么先
SET IDENTITY_INSERT orders OFF,再开customers;要么拆成两个独立批次(如用GO分隔) - 注意:存储过程中如果没配好
TRY...CATCH,异常退出可能导致IDENTITY_INSERT没被关掉,后续调用就卡住
实际迁移/修复场景中的常见坑
最常踩坑的地方不是语法,而是权限和上下文切换。比如用 SSMS 连接后手动执行没问题,但换成 SQL Agent 作业跑就失败——大概率是作业账户没被授予目标表的 ALTER 权限;又或者用 C# 的 SqlCommand 批量执行时,把 SET IDENTITY_INSERT 和 INSERT 写在不同 ExecuteNonQuery() 调用里,导致会话已断开,开关失效。
- SSIS 包里要插带 identity 的历史数据?必须在“执行 SQL 任务”中勾选
KeepIdentity = True,而不是靠SET IDENTITY_INSERT - 使用
bcp导入时加-E参数,等效于开启 identity 插入,此时不需要、也不能提前执行SET IDENTITY_INSERT - 临时表(
#tmp)也支持IDENTITY_INSERT,但作用域仅限当前会话,且不能跨批引用(比如在GO后再SET)
真正麻烦的从来不是怎么写那三行命令,而是谁在哪个上下文里执行、有没有权限、会不会被别的逻辑意外干扰。留心这些,比死记语法重要得多。










