在很多编程语言里,函数和过程几乎没啥区别。但在SQL里就不一样了。 在PostgreSQL里,函数和过程不仅仅是两种写代码的方式。这是两种完全不同的思维方式。
SQL里的函数不能改数据库里的数据。它只能处理传进来的数据,然后基于这些数据返回结果。函数就是为了在SELECT查询里用的。
SQL里的过程就是用来改数据库的。所以它可以操作事务(和函数不一样),还能往库里写点啥。但过程不能在SELECT查询里用。
下面是它们的简要对比:
| 特点 | 函数 (FUNCTION) | 过程 (PROCEDURE) |
|---|---|---|
| 返回数据 | ✅ 是的 (RETURNS ...) |
❌ 不会(只能做点事) |
| 怎么调用 | SELECT, PERFORM |
CALL |
| 能在查询里用吗 | ✅ 可以 | ❌ 不行 |
能在 DO 里用吗 |
✅ 可以 | ❌ 不行 |
支持 COMMIT, ROLLBACK |
❌ 不支持 | ✅ 支持 |
| PostgreSQL里啥时候有的 | 一开始就有 | 从11版本开始 |
SQL里的区别
在普通SQL里,函数就像表达式:它计算并返回一个值。 过程就是指令:它做点啥,但不会参与表达式。
SQL里的函数
SELECT calculate_discount(200);
- 可以用在
WHERE、ORDER BY、INSERT、UPDATE等等。 - 必须是纯函数:不能改数据库状态(如果IMMUTABLE/STABLE)。
SQL里的过程
CALL process_order(123);
- 不会返回结果。
- 可以做
COMMIT、ROLLBACK、调用RAISE、写循环啥的。
PL/pgSQL里的区别
PostgreSQL里的函数可以理解为一组计算。 它们非常灵活:可以传参数、用条件语句、循环、游标、子查询、返回行、标量、表。
PL/pgSQL里的函数
CREATE FUNCTION square(x INT) RETURNS INT AS $$
BEGIN
RETURN x * x;
END;
$$ LANGUAGE plpgsql;
特点:
- 必须有
RETURNS - 可以用
DECLARE、BEGIN、END、LOOP、IF、CASE - 不能执行
COMMIT/ROLLBACK - 可以在
SELECT、UPDATE、CHECK、WHERE、RETURNING里调用
调用:
SELECT square(5); -- 会返回25
PL/pgSQL里的过程
过程就是控制操作的机制。 它们特别适合:
- 执行多步逻辑;
- 批量更新和插入大量数据;
- 用事务控制:
COMMIT、ROLLBACK、SAVEPOINT。
CREATE PROCEDURE log_event(msg TEXT) AS $$
BEGIN
INSERT INTO logs(message) VALUES (msg);
COMMIT;
END;
$$ LANGUAGE plpgsql;
特点:
- 没有
RETURNS - 只能通过
CALL调用 - 允许用
COMMIT、ROLLBACK、SAVEPOINT - 适合批处理、迁移、ETL
调用:
CALL log_event('处理完成');
为啥要分函数和过程
因为它们在SQL里目标不一样:
| 函数 | 过程 |
|---|---|
| “算点东西然后返回” | “做点事但不返回结果” |
| SQL里调用 | 当命令调用 |
| 不能控制事务 | 能控制事务 |
用在 SELECT、JOIN、WHERE |
用在 CALL、脚本里 |
过程的核心优势 — COMMIT
过程可以自己管理事务。 也就是说,在过程里你可以直接:
BEGIN;
-- 逻辑
SAVEPOINT point1;
-- 尝试更新
ROLLBACK TO point1;
COMMIT;
而函数里禁止用 COMMIT 和 ROLLBACK。 如果你试了,会报错:ERROR: invalid transaction termination in function
这就意味着,函数必须是确定且安全的,而过程可以“脏活累活”——清理、记录日志、插入数据啥的。
对比表
| 特点 | FUNCTION |
PROCEDURE |
|---|---|---|
| 返回值 | ✅ RETURNS |
❌ |
能用在 SELECT |
✅ | ❌ |
| 调用方式 | SELECT、PERFORM、DO |
只能 CALL |
| 能用在触发器里 | ✅ | ❌(只能函数) |
内部事务(COMMIT) |
❌ 禁止 | ✅ 允许 |
| OUT参数用法 | 通过 RETURNS TABLE、RECORD |
直接用 OUT 参数 |
| 适合做计算 | ✅ | 🚫 不推荐 |
| 适合ETL、数据导入 | 🚫 有限制 | ✅ 完美 |
| 能用游标 | ✅ 可以 | ✅ 可以 |
啥时候用啥?
用函数,如果你:
- 想要返回值;
- 在
SELECT里调用、过滤数据; - 只是简单计算、校验或者SQL的封装。
用过程,如果你:
- 要做复杂操作;
- 需要事务控制;
- 处理批量、迁移、归档、日志啥的。
GO TO FULL VERSION