postgresql 15 和 16 的 sql 存储过程均不支持返回结果集,不可用 return query 或被 select 调用;唯一绕过方式是 refcursor,但需手动管理事务与游标;推荐改用 returns table 或 setof 的函数。

PostgreSQL 15 和 16 的 SQL 存储过程在核心能力上没有实质性差异:两者都不支持返回结果集,都不能用 RETURN QUERY,也不能被 SELECT 调用 —— 这不是 bug,是设计约束。
为什么 CALL 过程永远拿不到数据行
过程(CREATE PROCEDURE)在 PG 15 和 16 中语义一致:仅用于执行动作,不参与查询计划。即使你写一个只做 SELECT 1 的过程,也会报错:
-
ERROR: query has no destination for result data—— 因为裸SELECT必须有目标(INTO、PERFORM或返回给客户端) -
ERROR: RETURN QUERY is not allowed in procedures—— 过程体内禁止任何返回语句 -
CALL my_proc()执行后,客户端收不到任何行,也不进FROM子句
REFCURSOR 是唯一“绕过”限制的路径(但很重)
如果你非得用过程封装逻辑并“传出”结果,PG 15 和 16 都只支持 REFCURSOR,但需手动管理事务和游标生命周期:
- 必须显式
BEGIN/COMMIT(过程默认不自动开启事务) - 必须用
OPEN refcursor FOR SELECT ...,再由客户端用FETCH ALL IN :cursor_name取数 - 游标名需全局唯一或通过参数传入,否则并发调用会冲突
- 客户端必须记得
CLOSE,否则游标长期占用内存和连接资源
示例片段(PG 15/16 通用):
CREATE OR REPLACE PROCEDURE get_orders_cursor(OUT c refcursor) AS $$ BEGIN OPEN c FOR SELECT id, total FROM orders WHERE status = 'active'; END; $$ LANGUAGE plpgsql;
调用后需在同一个事务中 FETCH ALL IN "c" —— 这比直接用函数麻烦得多。
FUNCTION 才是返回结果集的实际选择
所有需要“返回表”的场景,应放弃过程,改用函数(CREATE FUNCTION)。PG 15 和 16 在此方面完全兼容,且更简洁安全:
-
RETURNS TABLE(...):定义列结构,调用时可直接SELECT * FROM f() -
RETURNS SETOF my_table:复用现有表结构,字段类型、NOT NULL约束全继承 - 函数可声明为
STABLE或IMMUTABLE,利于查询优化器重用执行计划 - 无游标生命周期负担,无显式事务要求(除非内部需要)
真正容易被忽略的是:哪怕你只是想“在过程里调用一次函数取结果”,也得用 PERFORM 吞掉它,或者把函数改成 OUT 参数模式 —— 过程本身对结果集是“免疫”的。










