写代码的时候有两件事特别重要:一是搞清楚到底发生了啥(尤其是当一切都不按你想的来时),二是能返回有用的数据。PL/pgSQL 里这点尤其明显,因为代码跑在服务器端,debug 起来没那么直观。这时候内置工具就很有用了:
RAISE NOTICE —— 这就是在函数执行时输出消息的方式。你可以把它当成 JavaScript 里的 console.log 或 Python 里的 print。你可以输出变量的值、当前执行状态,甚至给未来的自己留个“打招呼”。
RETURN QUERY —— 这是返回一组数据的方法,比如一整张表或者复杂查询的结果。有了它,PL/pgSQL 的函数就像 SQL 查询一样强大。
RAISE NOTICE 命令
RAISE NOTICE 允许你在函数执行过程中把消息输出到屏幕。格式长这样:
RAISE NOTICE '消息: %', 值;
%—— 这是变量的占位符,和 C 里的printf差不多。- 在消息文本后面可以列出你想插进去的变量。
使用示例
假设你写了个函数,要统计不同小组里的学生数量。你想看到中间结果,确认一切都在你的掌控中。
CREATE OR REPLACE FUNCTION count_students_in_groups() RETURNS VOID AS $$
DECLARE
group_name TEXT;
student_count INT;
BEGIN
FOR group_name IN SELECT DISTINCT group_name FROM students LOOP
SELECT COUNT(*) INTO student_count
FROM students WHERE group_name = group_name;
-- 输出结果
RAISE NOTICE '小组: %, 学生数量: %', group_name, student_count;
END LOOP;
END;
$$ LANGUAGE plpgsql;
当你调用这个函数时:
SELECT count_students_in_groups();
你会在日志里看到类似这样的消息:
NOTICE: 小组: 数学, 学生数量: 30
NOTICE: 小组: 哲学, 学生数量: 25
NOTICE: 小组: 生物, 学生数量: 18
注意,这个函数啥都不返回(它是 RETURNS VOID),但 RAISE NOTICE 能让你看到循环的进度。
RAISE 的一些小技巧
除了 NOTICE,你还可以用其他消息级别:
RAISE DEBUG—— 输出更多细节(只有日志级别设成 DEBUG 时才显示)。RAISE INFO—— 一般信息。RAISE WARNING—— 警告。RAISE EXCEPTION—— 抛出错误,这个我们后面会讲。
调试时最好用 NOTICE 或 DEBUG,因为它们不会中断函数执行,还挺方便的。
RETURN QUERY 命令:像大佬一样返回数据
RETURN QUERY 在 PL/pgSQL 里用来返回多行数据。你可以直接把 SQL 查询的结果从函数里返回。语法如下:
RETURN QUERY <SQL查询>;
你也可以组合多个查询:
RETURN QUERY <SQL查询1>;
RETURN QUERY <SQL查询2>;
例子1:带 RETURN QUERY 的函数
来写个函数,返回指定小组的学生列表。
CREATE OR REPLACE FUNCTION get_students_by_group(group_name TEXT)
RETURNS TABLE(id INT, name TEXT) AS $$
BEGIN
RETURN QUERY
SELECT id, name
FROM students
WHERE group_name = group_name;
END;
$$ LANGUAGE plpgsql;
现在调用这个函数:
SELECT * FROM get_students_by_group('数学');
为了测试函数,先建个 students 表并插入点数据:
CREATE TABLE students (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
group_name TEXT NOT NULL
);
INSERT INTO students (name, group_name) VALUES
('Otto Song', '物理'),
('Alex Lin', '数学'),
('Anna Vel', '数学'),
('Maria Chi', '历史');
结果:
| id | name |
|---|---|
| 2 | Alex Lin |
| 3 | Anna Vel |
你看,这个函数用起来就像普通 SQL 查询一样。
例子2:合并多个查询
如果你想返回多个表合并的数据怎么办?比如我们想返回学生和他们选的课程列表。
CREATE OR REPLACE FUNCTION get_students_and_courses()
RETURNS TABLE(student_name TEXT, course_name TEXT) AS $$
BEGIN
RETURN QUERY
SELECT s.name, c.name
FROM students s
JOIN enrollments e ON s.id = e.student_id
JOIN courses c ON e.course_id = c.id;
END;
$$ LANGUAGE plpgsql;
先建三个表:students、courses 和 enrollments,然后插入点数据:
-- 学生表
CREATE TABLE students (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL
);
-- 课程表
CREATE TABLE courses (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL
);
-- 选课表(关联表)
CREATE TABLE enrollments (
student_id INT REFERENCES students(id),
course_id INT REFERENCES courses(id)
);
-- 插入学生
INSERT INTO students (name) VALUES
('Otto Song'),
('Alex Lin'),
('Anna Vel'),
('Maria Chi');
-- 插入课程
INSERT INTO courses (name) VALUES
('数学'),
('物理'),
('历史');
-- 插入选课记录
INSERT INTO enrollments (student_id, course_id) VALUES
(1, 2), -- Otto -> 物理
(2, 1), -- Alex -> 数学
(3, 1), -- Anna -> 数学
(4, 3); -- Maria -> 历史
这时调用函数:
SELECT * FROM get_students_and_courses();
你会得到这样的结果:
| student_name | course_name |
|---|---|
| Otto Song | 物理 |
| Alex Lin | 数学 |
| Anna Vel | 数学 |
| Maria Chi | 历史 |
这个函数把三个表的数据合并起来,清楚地展示了每个学生选了什么课。
把 RAISE NOTICE 和 RETURN QUERY 结合用
有时候你会想在一个函数里同时用 RETURN QUERY 和 RAISE NOTICE,这样既能返回数据,又能看到执行过程中的消息。
比如下面这个函数,一边返回学生数据,一边输出消息让你知道进度:
CREATE OR REPLACE FUNCTION debug_students()
RETURNS TABLE(student_id INT, student_name TEXT) AS $$
DECLARE
count_students INT;
BEGIN
-- 统计学生数量
SELECT COUNT(*) INTO count_students FROM students;
RAISE NOTICE '学生总数: %', count_students;
-- 返回学生数据
RETURN QUERY
SELECT id, name FROM students;
RAISE NOTICE '函数执行完毕。';
END;
$$ LANGUAGE plpgsql;
如果 students 表还没建,先建一个并插入数据:
CREATE TABLE students (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL
);
INSERT INTO students (name) VALUES
('Otto Song'),
('Alex Lin'),
('Anna Vel'),
('Maria Chi');
现在调用函数:
SELECT * FROM debug_students();
你会同时看到数据和消息:
| student_id | student_name |
|---|---|
| 1 | Otto Song |
| 2 | Alex Lin |
| 3 | Anna Vel |
| 4 | Maria Chi |
控制台输出消息:
NOTICE: 学生总数: 4
NOTICE: 函数执行完毕。
常见错误
RAISE NOTICE 变量错误: 如果你忘了声明变量,或者变量名写错了,会报 variable does not exist 错。记得检查变量都声明对了。
返回类型错误: 如果你用了 RETURN QUERY,但函数没写 RETURNS TABLE,PostgreSQL 会报错。一定要保证返回类型和你实际返回的数据一致。
RAISE 占位符错误: 如果 % 占位符数量和变量数量不一致,也会报错。比如:
RAISE NOTICE '值: %, %', value1;
这样会因为少了第二个变量而报错。
GO TO FULL VERSION