SQL Server用SCOPE_IDENTITY()、MySQL用LAST_INSERT_ID()、PostgreSQL用RETURNING子句获取刚插入的自增ID,三者作用域机制不同,不可跨库通用,需按数据库类型选择对应方案。

SQL Server 中用 SCOPE_IDENTITY() 获取刚插入的自增 ID
在 SQL Server 存储过程中,SCOPE_IDENTITY() 是最安全、最常用的方式。它只返回当前作用域(即当前存储过程、批处理或触发器)内最后一条 INSERT 生成的自增值,不会受其他并发会话或触发器中插入的影响。
常见错误是误用 @@IDENTITY:它会返回当前会话中最后生成的自增 ID,哪怕是由触发器引发的插入——这极易导致取错值。
- 必须在
INSERT语句后**立即调用**,中间不能有其他影响标识列的操作(如另一条INSERT) - 如果表没有定义
IDENTITY列,SCOPE_IDENTITY()返回NULL - 返回类型为
numeric(38,0),建议显式转换为对应整型(如INT),避免下游类型不匹配
INSERT INTO users (name, email) VALUES ('Alice', 'a@example.com');
DECLARE @new_id INT = CAST(SCOPE_IDENTITY() AS INT);
MySQL 中用 LAST_INSERT_ID() 获取刚插入的自增 ID
MySQL 的 LAST_INSERT_ID() 是会话级函数,且**自动绑定到最近一次成功插入的自增列**,不需要参数,也不受触发器干扰(只要没手动调用 SET LAST_INSERT_ID(...))。这是它比 @@IDENTITY 更可靠的原因。
注意:该函数返回的是当前连接最后一次插入的自增 ID,不是全局最新值;多线程下只要各自用独立连接就互不干扰。
- 无需在
INSERT后立刻调用,但不能被同一连接中的其他INSERT覆盖 - 如果插入时显式指定了自增列的值(如
INSERT INTO t(id, name) VALUES (100, 'x')),LAST_INSERT_ID()仍返回该显式值(除非该值为 0 或 NULL 且启用了NO_AUTO_VALUE_ON_ZERO) - 在存储过程中可直接赋值给变量:
SELECT LAST_INSERT_ID() INTO new_id;
PostgreSQL 中用 RETURNING 子句一次性获取 ID
PostgreSQL 不依赖事后查询函数,而是通过 INSERT ... RETURNING 在插入时直接返回指定列(包括 id)。这是最高效、最原子的方式,避免了额外查询和竞态风险。
它比先 INSERT 再 SELECT currval('seq_name') 更安全:后者在并发下可能因序列跳号或事务回滚导致不一致。
-
RETURNING可返回多列甚至表达式,例如:RETURNING id, created_at, LOWER(name) - 若插入多行(
INSERT ... SELECT),RETURNING会返回所有新行的结果集,需用游标或WITH捕获 - 不能在函数外直接赋值给变量;在存储过程(
DO块或plpgsql函数)中需用INTO接收单行结果
INSERT INTO users (name, email) VALUES ('Bob', 'b@example.com') RETURNING id;
跨数据库兼容性差,别硬套同一个函数名
不同数据库对“刚插入 ID”的抽象完全不同:SCOPE_IDENTITY() 是 SQL Server 特有,LAST_INSERT_ID() 是 MySQL 特有,PostgreSQL 根本没有等价函数——它用 RETURNING 改变了整个交互范式。
这意味着:写通用 DAL 层时,不能靠统一包装一个叫 get_last_insert_id() 的函数来掩盖差异;实际逻辑必须按数据库方言分支处理。尤其要注意 PostgreSQL 的 RETURNING 必须和 INSERT 写在同一语句里,没法像其他数据库那样拆成两步。
最容易被忽略的是事务边界:比如在 SQL Server 中,如果 INSERT 和 SCOPE_IDENTITY() 被分在两个独立的 EXEC 或远程调用中,作用域已丢失,必然拿不到值。











