sql server用scope_identity()、mysql用last_insert_id()、postgresql用returning子句获取刚插入的自增id,三者作用域机制不同,不可跨库通用,需按数据库类型选择对应方案。

SQL Server 中用 SCOPE_IDENTITY() 获取刚插入的自增 ID
在 SQL Server 存储过程中,SCOPE_IDENTITY() 是最安全、最常用的方式——它只返回当前作用域(即当前存储过程、批处理或触发器)中最后一条 INSERT 生成的自增值。不会受其他并发会话或触发器内插入的影响。
常见错误是误用 @@IDENTITY:如果表上有触发器,且触发器又往另一张带自增列的表里插了数据,@@IDENTITY 就会返回触发器里的 ID,而不是你期望的主表 ID。
示例:
INSERT INTO Users (Name, Email) VALUES ('Alice', 'a@example.com');
SELECT @newId = SCOPE_IDENTITY();
- 必须在
INSERT后**立即**调用,中间不能有其他影响标识列的操作(如另一条INSERT) - 返回值类型为
numeric(38,0),建议用INT或BIGINT变量接收,注意类型匹配 - 如果插入的是空行(比如
INSERT ... SELECT但没查到数据),SCOPE_IDENTITY()返回NULL,需提前判断
MySQL 中用 LAST_INSERT_ID() 获取上一次 INSERT 的自增 ID
MySQL 没有作用域概念,LAST_INSERT_ID() 是**会话级**的,只要你在同一个连接里执行 INSERT,之后调用它就能拿到对应 ID。它不依赖语句是否在存储过程中,也不受触发器干扰(前提是触发器没显式调用 INSERT ... VALUES(...) 并触发自增)。
关键点:
- 不需要参数,直接写
SELECT LAST_INSERT_ID();或赋值给变量:SET @new_id = LAST_INSERT_ID(); - 即使你执行了
INSERT ... ON DUPLICATE KEY UPDATE,只要实际发生了新插入,ID 仍会被更新;如果是纯更新,则LAST_INSERT_ID()不变 - 如果插入多行(
INSERT INTO t VALUES (1),(2),(3)),它只返回**第一行生成的 ID**,不是最大 ID - 不要用
SELECT MAX(id)替代——在高并发下可能拿到别人插入的 ID
PostgreSQL 中用 RETURNING 子句直接取回自增 ID
PostgreSQL 不提供类似 SCOPE_IDENTITY() 的函数,而是推荐在 INSERT 语句末尾加上 RETURNING,一次性完成插入和取 ID,原子性强、无竞态风险。
示例:
INSERT INTO users (name, email) VALUES ('Bob', 'b@example.com') RETURNING id;
在存储过程(或函数)中,可以用 RETURNING 赋值给变量:
INSERT INTO users (name, email) VALUES ('Bob', 'b@example.com') RETURNING id INTO new_id;
-
RETURNING支持返回多列,比如RETURNING id, created_at - 如果插入失败(违反约束),整个语句回滚,不会产生 ID,也不会赋值
- 不能用于批量插入后统一取 ID——每条
INSERT都得单独加RETURNING,或改用INSERT ... SELECT+ CTE 方式
跨数据库兼容性差,别硬套同一套逻辑
没有一种写法能在 SQL Server、MySQL、PostgreSQL 里通用。试图用 @@IDENTITY 去跑 MySQL 会报错;在 PostgreSQL 里用 LAST_INSERT_ID() 根本不存在。
如果你的应用要支持多种数据库:
- ORM 层(如 Entity Framework、Django ORM、MyBatis)通常已封装好适配逻辑,优先走 ORM 的 insert + return ID 流程
- 纯 SQL 场景下,必须按目标数据库选对应方案,不能抽象成“一个函数名解决所有”
- 特别注意 PostgreSQL 的
RETURNING是语句一部分,不能拆成两条独立语句;而 SQL Server 和 MySQL 的函数调用可以分开写
最容易被忽略的是:有些开发者在存储过程中先 INSERT,再做其他耗时操作(比如发消息、调外部 API),最后才去取 ID——这期间若有异常或事务中断,ID 就丢了,且无法重试。真正安全的做法是让 ID 获取紧贴插入动作,越近越好。










