CodeGym /課程 /SQL SELF /RAISE NOTICE、RETURN QUERY 指令

RAISE NOTICE、RETURN QUERY 指令

SQL SELF
等級 50 , 課堂 3
開放

寫程式最重要的兩件事:一是搞懂現在發生什麼事(尤其當一切都不照你想的走時),二是能回傳有用的資料。這在 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 時建議用 NOTICEDEBUG,因為它們不會中斷 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 NOTICERETURN QUERY 一起用

有時 RETURN QUERYRAISE 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;

這會報錯,因為少了一個變數。

留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION