postgresql insert ... returning 可返回插入行的任意字段,包括自增主键、默认值(如 current_timestamp)、表达式结果及序列值;支持 returning * 或指定列,批量插入时原子返回每行对应结果。

PostgreSQL INSERT ... RETURNING 能返回哪些字段
直接返回刚插入的行数据,包括自增主键、默认值(如 CURRENT_TIMESTAMP)、表达式计算结果,甚至序列函数(如 nextval())的值。它不是只返回 ID,而是整行或指定列 —— 只要你在 RETURNING 后面写出来,且该列在插入时已确定值,就能拿到。
常见误区是以为只能返回 id;其实你可以写 RETURNING id, created_at, status,甚至 RETURNING *, md5(email)(只要表达式在插入后可求值)。
-
RETURNING *返回完整新行,但注意:未在INSERT中显式赋值、又无默认值的列会是NULL - 如果插入时用了
DEFAULT或省略某列,而该列有DEFAULT now()或serial,它的值会在RETURNING中体现 - 不能返回触发器中修改的字段(除非用
AFTER触发器配合RETURNING以外的方式)
RETURNING 在批量 INSERT 中怎么用
支持一次插入多行并全部返回,语法不变,但要注意结果集行为:每插入一行,就返回对应一行。这和单行 INSERT 完全一致,无需额外封装逻辑。
例如执行:
INSERT INTO users (name, email) VALUES
('Alice', 'a@example.com'),
('Bob', 'b@example.com'),
('Charlie', 'c@example.com')
RETURNING id, name, created_at;会返回三行结果,顺序与 VALUES 中一致。
- 批量插入 +
RETURNING是原子的,不会出现部分行返回、部分没返回的情况 - 若其中某行违反约束(如唯一键冲突),整个语句报错,不返回任何行 —— 没有“成功插入的那几行先返回”的行为
- 想跳过冲突继续插其他行?得用
ON CONFLICT DO NOTHING或DO UPDATE,但注意:只有实际插入或更新的行才会出现在RETURNING结果里
RETURNING 和 LASTVAL() / CURRVAL() 的关键区别
RETURNING 是声明式、安全、精确的;LASTVAL() 和 CURRVAL() 是状态式、会话级、易出错的。别再用后者替代前者。
-
RETURNING拿到的是本次INSERT真实生成的值,不受其他并发会话干扰 -
LASTVAL()返回当前会话中最近一次序列取值(不管是不是你自己的语句),一旦中间有别人也调了NEXTVAL(),就错乱了 -
CURRVAL()要求该会话此前必须调过同序列的NEXTVAL(),否则报错currval of sequence "xxx" is not yet defined in this session - 如果你用的是
IDENTITY列(GENERATED ALWAYS AS IDENTITY),LASTVAL()根本不适用 —— 它只对显式调用NEXTVAL()的序列有效
应用层调用 RETURNING 时要注意什么
大多数 PostgreSQL 客户端(如 psycopg2、pgx、node-postgres)都原生支持获取 RETURNING 结果,但需主动读取结果集,不是“自动塞进 lastInsertId”那种模式。
- 在
psycopg2中,用cursor.fetchall()或cursor.fetchone()获取返回行,不是看cursor.rowcount - 在
node-postgres中,result.rows就是RETURNING的数据,result.rowCount是插入行数,二者可能不同(比如ON CONFLICT DO UPDATE场景) - 不要把
RETURNING当作“副作用”忽略 —— 如果忘了处理结果集,有些驱动(如旧版 libpq)可能卡住或报错 - 若插入后立刻需要新记录的
id去关联其他表,直接用RETURNING最稳;别先插、再查、再用,那是竞态漏洞温床
最常被忽略的一点:RETURNING 的字段名就是结果集的列名,不带表别名前缀;如果有多表 INSERT(如 CTE 中的 INSERT),列名按 RETURNING 子句中写的为准,不是源表字段名。










