能,PostgreSQL中INSERT...RETURNING可直接原子性获取新ID,无需额外查询,且不受并发干扰;需确保目标列为SERIAL、IDENTITY或带序列默认值,并显式指定RETURNING id。

PostgreSQL 中 INSERT ... RETURNING 能直接拿到新 ID 吗?
能,而且这是最稳妥的方式。相比先 INSERT 再 SELECT LASTVAL() 或 CURRVAL(),RETURNING 是原子操作,不会被并发插入干扰,也不依赖序列状态。
关键点在于:必须明确指定要返回的列,且该列得是自增字段(如 SERIAL、GENERATED ALWAYS AS IDENTITY 或显式绑定序列的列)。
-
RETURNING只在支持它的数据库中可用(PostgreSQL 原生支持,MySQL 不支持,SQLite 仅从 3.35.0 起部分支持) - 不能只写
RETURNING *就完事——如果表有默认值或表达式列,可能返回意外内容;建议明确写RETURNING id - 若插入多行,
RETURNING会返回多行结果,不是单个值
MySQL 怎么办?它不支持 RETURNING,有没有替代方案?
MySQL 确实不支持 INSERT ... RETURNING(截至 8.4 仍无),必须分两步走,但要注意 LAST_INSERT_ID() 的作用域和并发安全边界。
LAST_INSERT_ID() 返回的是当前会话最后一次 INSERT 生成的 AUTO_INCREMENT 值,前提是没显式指定该列值(即让 MySQL 自动分配)。一旦你写了 INSERT INTO t(id, name) VALUES (100, 'x'),LAST_INSERT_ID() 就不会更新。
- 必须在同一线程/连接中紧接
INSERT后调用SELECT LAST_INSERT_ID(),中间不能执行其他影响 AUTO_INCREMENT 的语句 - 不能跨连接使用,不同连接的
LAST_INSERT_ID()互不影响 - 批量插入时(
INSERT ... VALUES (...), (...)),LAST_INSERT_ID()返回的是第一行的 ID,不是最大 ID
INSERT ... RETURNING 在 PostgreSQL 里怎么写才不出错?
常见错误是忽略目标列是否真能“返回”——比如试图对没有默认值或序列的普通 INTEGER 列用 RETURNING id,结果返回 NULL。
正确写法取决于建表方式:
- 用
SERIAL:CREATE TABLE t (id SERIAL PRIMARY KEY, name TEXT);→INSERT INTO t(name) VALUES ('abc') RETURNING id; - 用
IDENTITY:id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY→ 同样支持RETURNING id - 用显式序列:
id INT DEFAULT nextval('t_id_seq')→ 也支持,但需确保DEFAULT真被触发(即没在INSERT中显式提供id值)
如果插入时手动指定了 id 值,RETURNING id 会返回你写的值,不是“生成”的——这点容易误解。
ORM 框架里怎么利用 RETURNING?以 SQLAlchemy 为例
SQLAlchemy 2.0+ 默认启用 RETURNING(PostgreSQL 下),但需要显式开启并注意返回类型。
例如用 insert(...).returning(...) 构造语句:
stmt = insert(users).values(name='alice').returning(users.c.id) result = conn.execute(stmt) new_id = result.scalar_one() # 注意:单行用 scalar_one(),多行用 all()
- 如果表定义里没设
autoincrement=True或没配好序列,RETURNING可能拿不到值 - 用
session.execute()时,记得加execution_options={'populate_existing': True}避免缓存干扰 - 异步驱动(
asyncpg)下,RETURNING依然生效,但返回的是Result对象,需 await fetchone() 或 fetchall()
真正容易被忽略的是:RETURNING 不是“自动附加”的功能,它依赖底层方言和表结构共同配合——哪怕 PostgreSQL 支持,表没设好主键或序列,照样返回空。

















