postgresql 15 支持 call 语句调用 create procedure 定义的存储过程,但不支持对函数使用 call;procedure 可含 commit/rollback,不可返回值,必须在顶层事务块中显式用 begin/call/commit 调用。

PostgreSQL 15里没有CALL语句?先确认存储过程类型
PostgreSQL 15 确实支持 CREATE PROCEDURE,但调用方式和函数完全不同:它不支持标准 SQL 的 CALL 语法(除非你用的是带 PL/pgSQL 的扩展或外部工具封装)。真正能直接执行的,是用 CALL 调用 PROCEDURE 对象——但这要求你用的是 PostgreSQL 原生的 PROCEDURE(不是 FUNCTION),且必须在事务块中运行。
常见误判点:把 CREATE FUNCTION 当成存储过程去 CALL,结果报错 ERROR: CALL is not supported for functions。记住:只有 CREATE PROCEDURE 定义的对象才接受 CALL,而且不能返回值。
怎么写一个能被CALL执行的存储过程?
必须用 CREATE PROCEDURE,不能用 CREATE FUNCTION;过程体里可以有 DML、事务控制(COMMIT/ROLLBACK),但不能用 RETURN 返回数据。示例:
CREATE OR REPLACE PROCEDURE update_user_status(user_id INT, new_status TEXT) LANGUAGE plpgsql AS $$ BEGIN UPDATE users SET status = new_status WHERE id = user_id; COMMIT; -- 允许显式提交 END; $$;
关键点:
-
LANGUAGE plpgsql是最常用选项,其他如sql也支持,但功能受限(比如不能用COMMIT) - 过程参数默认是
IN模式,不支持OUT或INOUT—— 所以别指望它“返回”结果集 - 过程名和参数类型要和调用时完全一致,否则报错
procedure ... does not exist
调用时为什么总报错“cannot be executed from a function or procedure”?
这是最常踩的坑:PostgreSQL 要求 CALL 必须在顶层事务块中执行,不能嵌套在函数、触发器或另一个过程内部。也就是说,你不能在 CREATE FUNCTION 里写 CALL update_user_status(...)。
正确调用方式只有这一种:
BEGIN; CALL update_user_status(123, 'active'); COMMIT;
注意:
-
CALL本身不开启事务,必须显式用BEGIN/COMMIT包裹 - 如果过程里已有
COMMIT,外面再COMMIT会报错cannot commit while in a cursor—— 这时候该删掉外层事务控制 - psql 中可以用
\set AUTOCOMMIT off配合手动事务,但脚本里建议显式写BEGIN
想返回结果怎么办?别硬套CALL,改用FUNCTION
如果你需要返回数据(比如查询结果、计数、状态码),PROCEDURE 就不是合适选择。PostgreSQL 的 FUNCTION 更灵活:
CREATE OR REPLACE FUNCTION get_user_count(status_filter TEXT DEFAULT 'active') RETURNS INTEGER LANGUAGE sql AS $$ SELECT COUNT(*) FROM users WHERE status = status_filter; $$;
然后直接用:
SELECT get_user_count('inactive');
区别很实在:
-
FUNCTION可以SELECT、可返回值、可嵌套调用、支持OUT参数 -
PROCEDURE专为“执行副作用”设计(改数据、发通知、清理临时表),适合做事务性操作 - 别为了“看起来像存储过程”而强行用
PROCEDURE,反而绕远路
真正复杂的地方在于事务边界控制——过程里 COMMIT 和外层事务的冲突,比语法本身更难调试。动手前先想清楚:这一步到底需不需要中途提交?还是整个操作应该原子化?










