寫程式最重要的兩件事:一是搞懂現在發生什麼事(尤其當一切都不照你想的走時),二是能回傳有用的資料。這在 PL/pgSQL 裡超級重要,因為你寫的東西都在 server 端跑,debug 真的不太直覺。這時就要靠內建工具啦:
RAISE NOTICE —— 這就是在 function 執行時印出訊息的方式。你可以把它想成 JavaScript 的 console.log 或 Python 的 print。你可以秀變數值、目前執行狀態,或是留個「哈囉未來的我」訊息。
RETURN QUERY —— 這是回傳一整個資料集的方法,比如一整張表或複雜查詢的結果。有了它,PL/pgSQL function 就像真的 SQL 查詢一樣強大。
RAISE NOTICE 指令
RAISE NOTICE 讓你在 function 執行時把訊息印到螢幕上。格式長這樣:
RAISE NOTICE '訊息: %', 變數值;
%—— 這是變數的 placeholder,有點像 C 裡的printf。- 訊息文字後面可以列出你要帶進去的變數。
使用範例
假設你寫一個 function,要算不同 group 裡的學生人數。你想看一下中間的值,確定一切都照你預期走。
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;
當你呼叫這個 function:
SELECT count_students_in_groups();
你會在 log 裡看到類似這樣的訊息:
NOTICE: 群組: 數學, 學生數量: 30
NOTICE: 群組: 哲學, 學生數量: 25
NOTICE: 群組: 生物學, 學生數量: 18
注意這個 function 沒有回傳東西(因為 RETURNS VOID),但 RAISE NOTICE 讓你看到迴圈跑到哪。
RAISE 的小技巧
除了 NOTICE,你還可以用其他訊息等級:
RAISE DEBUG—— 額外資訊(只有 log level 設 DEBUG 才會顯示)。RAISE INFO—— 一般資訊。RAISE WARNING—— 警告。RAISE EXCEPTION—— 丟出錯誤(這之後會講)。
debug 時建議用 NOTICE 或 DEBUG,因為它們不會中斷 function 執行又很好用。
RETURN QUERY 指令:資料回傳就像大師
RETURN QUERY 在 PL/pgSQL 裡用來回傳一組 row。你可以直接把 SQL 查詢結果從 function 丟回去。語法如下:
RETURN QUERY <SQL 查詢>;
你也可以合併多個查詢:
RETURN QUERY <SQL 查詢 1>;
RETURN QUERY <SQL 查詢 2>;
範例 1:用 RETURN QUERY 的 function
來寫一個 function,回傳指定群組的學生名單。
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;
現在呼叫這個 function:
SELECT * FROM get_students_by_group('數學');
要測試 function,先建立 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 |
你看,這 function 跟一般 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 -> 歷史
這時呼叫 function:
SELECT * FROM get_students_and_courses();
你會拿到這樣的結果:
| student_name | course_name |
|---|---|
| Otto Song | 物理 |
| Alex Lin | 數學 |
| Anna Vel | 數學 |
| Maria Chi | 歷史 |
這 function 很優雅地把三個表的資料合起來,讓你一眼看出學生選了哪些課。
RAISE NOTICE 跟 RETURN QUERY 一起用
有時 RETURN QUERY 跟 RAISE NOTICE 可以在同一個 function 裡合作,讓你邊控制執行流程邊看到中間結果。
來看一個 function,會回傳學生資料同時印出訊息,讓你知道進度:
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 'Function 執行結束。';
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');
現在呼叫 function:
SELECT * FROM debug_students();
你會同時拿到資料和訊息:
| student_id | student_name |
|---|---|
| 1 | Otto Song |
| 2 | Alex Lin |
| 3 | Anna Vel |
| 4 | Maria Chi |
console 會印出:
NOTICE: 總學生數: 4
NOTICE: Function 執行結束。
常見錯誤
RAISE NOTICE 變數錯誤: 如果你忘了宣告變數或拼錯名字,會出現 variable does not exist 錯誤。記得檢查變數都正確宣告。
回傳型態錯誤: 如果你用 RETURN QUERY,但 function 沒寫 RETURNS TABLE,PostgreSQL 會報錯。要確保回傳型態跟資料一致。
RAISE placeholder 錯誤: 如果 % 的數量跟變數不符會出錯。例如:
RAISE NOTICE '值: %, %', value1;
這會報錯,因為少了一個變數。
GO TO FULL VERSION