SQL Server中用SCOPE_IDENTITY()最安全,它仅返回当前作用域内最后一次INSERT生成的自增ID,不受触发器或并发干扰;必须紧接INSERT后立即调用,返回numeric(38,0)类型,需显式转换。

SQL Server 中用 SCOPE_IDENTITY() 获取刚插入的自增 ID
在 SQL Server 存储过程中,SCOPE_IDENTITY() 是最安全、最常用的方式。它只返回当前作用域(即当前存储过程、批处理或触发器)内最后一次 INSERT 生成的自增值,不会受其他并发会话或触发器中插入的影响。
常见错误是误用 @@IDENTITY:它会返回当前会话中**任意作用域**最后生成的 ID,如果表上有触发器又执行了插入,就可能拿到触发器里产生的 ID,而不是你期望的主表 ID。
- 必须在
INSERT语句之后**立即调用**,中间不能有其他影响标识列的操作(如另一条INSERT) - 返回类型为
numeric(38,0),建议显式转换成目标列类型(如INT),避免后续计算隐式转换出错 - 如果插入失败(如违反约束),
SCOPE_IDENTITY()仍返回上一次成功插入的值——所以务必先检查@@ROWCOUNT或用TRY...CATCH
MySQL 中用 LAST_INSERT_ID() 获取自增 ID
MySQL 的 LAST_INSERT_ID() 是会话级函数,返回当前连接中最近一次 INSERT 或 REPLACE 生成的自增值,且不受触发器干扰(这点比 SQL Server 的 @@IDENTITY 更可靠)。
注意它不依赖作用域,而是依赖“连接”;只要没被同连接的其他 INSERT 覆盖,就能取到值。但它对多行插入只返回第一行的 ID,不是最大值。
- 无需额外参数,直接写
SELECT LAST_INSERT_ID();即可 - 在存储过程中赋值给变量时,推荐写成
SET @new_id = LAST_INSERT_ID(); - 如果插入语句用了
INSERT ... SELECT或INSERT ... ON DUPLICATE KEY UPDATE,行为不同:后者只有发生插入时才更新LAST_INSERT_ID(),更新时不改变它
PostgreSQL 中用 RETURNING 子句直接返回 ID
PostgreSQL 不提供类似 SCOPE_IDENTITY() 的函数,而是推荐在 INSERT 语句末尾加上 RETURNING 子句,一次性完成插入并获取值。这是最原子、最直观的方式,也支持返回多列甚至表达式。
相比先 INSERT 再 SELECT currval(),RETURNING 不需要知道序列名,也不怕并发竞争或事务回滚后序列值“丢失”的问题。
- 基本写法:
INSERT INTO users (name) VALUES ('Alice') RETURNING id; - 在存储过程(PL/pgSQL)中可直接赋值:
INSERT INTO logs (msg) VALUES ('start') RETURNING id INTO v_log_id; - 若表没有显式定义序列(比如用
GENERATED ALWAYS AS IDENTITY),RETURNING同样有效
跨数据库兼容性差,别硬套同一套逻辑
没有通用函数能安全覆盖 SQL Server、MySQL、PostgreSQL 和 Oracle。比如 Oracle 用 RETURNING INTO(类似 PG),而 SQLite 也支持 RETURNING(3.35+),但旧版本只能用 last_insert_rowid()。
如果你的代码要适配多个数据库,不要试图封装一个“统一获取 ID”的函数——容易在边界场景出错。更实际的做法是:按目标数据库选对应方案,并在部署前确认驱动/ORM 是否已帮你处理(例如 Entity Framework 的 SaveChanges() 自动映射,Django 的 save() 返回主键)。
最容易被忽略的是事务回滚后的状态:SQL Server 的 SCOPE_IDENTITY() 在回滚后依然保留原值;MySQL 的 LAST_INSERT_ID() 不受回滚影响;而 PostgreSQL 的 RETURNING 只在语句成功执行时返回,天然与事务一致。

















