前面几节我们已经聊过PostgreSQL里的过程和函数了。现在是时候更深入地研究一下它们了。
函数和过程可以各自独立运行,但大多数时候,它们的配合才决定了整个系统的成败。最方便的地方就是函数可以互相调用,传递数据,甚至还能拿到执行结果。
函数 vs 过程:区别在哪?
来回顾一下,在PostgreSQL里函数和过程到底有啥区别:
函数(
FUNCTION):- 会返回值。
- 可以在
SELECT里用。 - 经常用来做计算或者数据转换。
过程(
PROCEDURE):- 不会直接返回值。
- 用来执行操作,比如插入、更新或者删除数据。
- 要用
CALL命令来调用。
函数之间的数据传递
说到实践,先来看个基础例子,演示一下函数和过程之间怎么传递数据。其实,函数之间的数据传递主要靠参数和返回值。
下面是一个函数里调用另一个函数的例子:
CREATE OR REPLACE FUNCTION get_student_name(student_id INT)
RETURNS TEXT AS $$
DECLARE
student_name TEXT;
BEGIN
-- 根据ID查学生名字
SELECT name INTO student_name FROM students WHERE id = student_id;
-- 返回名字
RETURN student_name;
END;
$$ LANGUAGE plpgsql;
这个函数可以在另一个函数里被调用:
CREATE OR REPLACE FUNCTION welcome_student(student_id INT)
RETURNS TEXT AS $$
DECLARE
message TEXT;
BEGIN
-- 用另一个函数拿到学生名字
message := '欢迎, ' || get_student_name(student_id) || '!';
-- 返回欢迎语
RETURN message;
END;
$$ LANGUAGE plpgsql;
get_student_name函数根据学生ID返回名字(student_id)。- 另一个函数
welcome_student用这个名字来生成欢迎消息。
注意:用SELECT INTO可以把查询结果存到PL/pgSQL变量里。
从函数里调用过程的例子
现在来看看怎么在函数里调用过程。假设我们有个过程,用来记录学生登录系统的时间:
CREATE OR REPLACE PROCEDURE log_student_entry(student_id INT)
LANGUAGE plpgsql
AS $$
BEGIN
INSERT INTO log_entries(student_id, entry_time)
VALUES (student_id, NOW());
END;
$$;
现在我们在函数里调用这个过程,让它记录登录并返回消息:
CREATE OR REPLACE FUNCTION student_login(student_id INT)
RETURNS TEXT AS $$
BEGIN
-- 调用过程记录日志
CALL log_student_entry(student_id);
-- 返回消息
RETURN '学生登录已成功记录。';
END;
$$ LANGUAGE plpgsql;
交互的实际例子
例子1:计算订单总额并记录日志
假设你在做一个在线订单系统。用这个函数来算订单的总额:
CREATE OR REPLACE FUNCTION calculate_order_total(order_id INT)
RETURNS NUMERIC AS $$
DECLARE
total NUMERIC;
BEGIN
-- 把所有订单项的金额加起来
SELECT SUM(price * quantity) INTO total
FROM order_items
WHERE order_id = order_id;
RETURN total;
END;
$$ LANGUAGE plpgsql;
用这个过程来保存订单总额:
CREATE OR REPLACE PROCEDURE log_order_total(order_id INT, total NUMERIC)
LANGUAGE plpgsql
AS $$
BEGIN
INSERT INTO order_totals(order_id, total)
VALUES (order_id, total);
END;
$$;
现在把它们串起来:
CREATE OR REPLACE FUNCTION process_order(order_id INT)
RETURNS TEXT AS $$
DECLARE
total NUMERIC;
BEGIN
-- 调用函数算总额
total := calculate_order_total(order_id);
-- 用过程记录总额
CALL log_order_total(order_id, total);
RETURN '订单处理成功。';
END;
$$ LANGUAGE plpgsql;
例子2:获取学生最高评分并更新资料
获取最高评分的函数:
CREATE OR REPLACE FUNCTION get_highest_rating(student_id INT)
RETURNS INT AS $$
DECLARE
max_rating INT;
BEGIN
-- 查找学生的最高评分
SELECT MAX(rating) INTO max_rating
FROM ratings
WHERE student_id = student_id;
RETURN max_rating;
END;
$$ LANGUAGE plpgsql;
更新学生资料的过程:
CREATE OR REPLACE PROCEDURE update_student_profile(student_id INT, max_rating INT)
LANGUAGE plpgsql
AS $$
BEGIN
UPDATE students
SET highest_rating = max_rating
WHERE id = student_id;
END;
$$;
调用这两个操作的函数:
CREATE OR REPLACE FUNCTION refresh_student_profile(student_id INT)
RETURNS TEXT AS $$
DECLARE
max_rating INT;
BEGIN
-- 获取最高评分
max_rating := get_highest_rating(student_id);
-- 更新学生资料
CALL update_student_profile(student_id, max_rating);
RETURN '资料已成功更新。';
END;
$$ LANGUAGE plpgsql;
交互时的常见错误
最常见的错误之一就是函数和过程之间的数据类型不匹配。比如你的过程需要NUMERIC类型参数,但你传了INTEGER,PostgreSQL就会报类型不匹配。一定要检查数据类型一致。
还有一种错误是循环调用,比如函数A调用函数B,B又反过来调用A。这样会导致无限递归,系统直接崩溃。
实际意义
为啥要这么交互?在现实开发里,函数和过程就像“积木”一样搭建复杂系统。这样可以把代码拆成独立的小块,方便调试、复用和测试。比如:
- 面试时可能让你写个函数,里面要调用过程来做复杂操作。能展示你会用这些交互,绝对加分。
- 做实际项目,比如电商、日志系统或者CRM,合理组织函数和过程的配合,代码会清晰很多。
想继续深入了解函数和过程的交互,可以看看PL/pgSQL官方文档。
GO TO FULL VERSION