CodeGym /课程 /SQL SELF /函数和过程之间的交互

函数和过程之间的交互

SQL SELF
第 55 级 , 课程 1
可用

前面几节我们已经聊过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;
  1. get_student_name函数根据学生ID返回名字(student_id)。
  2. 另一个函数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官方文档

评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION