想像一下,你寫了一個 function 要算學生的平均分數。如果你不小心除以零(比如沒成績),會發生什麼事?這種 code 如果直接丟到 production,bug 馬上就來敲門啦。PL/pgSQL 有很強大的錯誤處理工具,讓你的 code 更穩、更安全,也更好維護。
PL/pgSQL 的錯誤處理可以:
- 產生訊息,說明哪裡出錯。
- 遇到嚴重錯誤時直接中止執行。
- 把問題記錄下來,方便之後分析。
PL/pgSQL 的訊息等級
PL/pgSQL 支援幾種訊息等級,幫助開發者 debug 跟排除問題。如下:
- NOTICE:顯示資訊訊息,通常用來 debug。
- WARNING:提醒有潛在問題,但不會中斷程式。
- EXCEPTION:嚴重錯誤,會中止程式(然後把控制權還給呼叫端)。
PL/pgSQL 的訊息等級
| 訊息等級 | 說明 |
|---|---|
NOTICE |
資訊或 debug 訊息,不影響執行 |
WARNING |
可能有問題的提醒,像提示一樣 |
EXCEPTION |
嚴重錯誤,會終止程式執行 |
RAISE 指令語法
要產生訊息或處理錯誤,用 RAISE 指令。基本語法如下:
RAISE <訊息等級> '訊息內容' [, 變數...];
<訊息等級>—NOTICE、WARNING、EXCEPTION。'訊息內容'— 問題描述。[變數...]— 可以插入訊息內容的變數。
範例 1:用 RAISE NOTICE
有時候你想知道 function 裡面發生什麼事。比如 debug 迴圈:
DO $$
BEGIN
FOR i IN 1..5 LOOP
RAISE NOTICE '目前 i 的值: %', i;
END LOOP;
END
$$;
結果:console 會顯示 目前 i 的值: 1、目前 i 的值: 2,一直到 5。
範例 2:用 RAISE EXCEPTION
假設你寫一個 function,遇到某些狀況就要直接報錯:
DO $$
BEGIN
IF 1 = 1 THEN
RAISE EXCEPTION '有什麼地方怪怪的!';
END IF;
END
$$;
結果:執行會中斷,錯誤訊息會顯示在 console。
在 RAISE 裡用參數
你可以用參數讓訊息內容更個人化。用 % 當 placeholder:
範例 3:在 RAISE 插入變數
DO $$
DECLARE
student_name TEXT := '伊凡';
average_score NUMERIC := NULL;
BEGIN
IF average_score IS NULL THEN
RAISE EXCEPTION '學生 % 沒有平均分數!', student_name;
END IF;
END
$$;
結果:訊息 學生 伊凡 沒有平均分數!。
你看,% 會被 student_name 取代,讓訊息更有意義。
自訂錯誤產生
錯誤不一定是意外!有時候你要故意產生錯誤,保護 code 不被錯的資料搞壞。
範例 4:檢查輸入值
寫個 function,檢查數字是不是負的,如果是就報錯:
CREATE OR REPLACE FUNCTION check_positive(value NUMERIC)
RETURNS TEXT AS $$
BEGIN
IF value < 0 THEN
RAISE EXCEPTION '數字 % 是負的!', value;
END IF;
RETURN '數字沒問題。';
END;
$$ LANGUAGE plpgsql;
來測試 function:
SELECT check_positive(-5);
結果:錯誤訊息 數字 -5 是負的!。
如果傳正數:
SELECT check_positive(10);
結果:數字沒問題。
錯誤處理的情境
你會產生錯誤已經很棒了,更厲害的是根據情況處理錯誤。這時就要用 BEGIN ... EXCEPTION 區塊。
錯誤處理結構
BEGIN
-- 你的主要程式碼
EXCEPTION
WHEN 錯誤類型 THEN
-- 發生這個錯誤要做什麼
WHEN 其他錯誤 THEN
-- 發生其他錯誤要做什麼
WHEN OTHERS THEN
-- 處理所有沒特別寫的錯誤
END;
來解釋一下:
EXCEPTION— 關鍵字,表示開始處理錯誤。WHEN— 指定要處理哪種錯誤,例如unique_violation或division_by_zero。OTHERS— 處理所有沒特別寫的錯誤。
範例 5:處理除以零
來個簡單的除法 function,示範錯誤處理:
CREATE OR REPLACE FUNCTION safe_divide(a NUMERIC, b NUMERIC)
RETURNS NUMERIC AS $$
BEGIN
-- 嘗試做除法
RETURN a / b;
EXCEPTION
WHEN division_by_zero THEN
RAISE WARNING '試圖除以零,回傳 NULL。';
RETURN NULL;
END;
$$ LANGUAGE plpgsql;
測試 function:
SELECT safe_divide(10, 2); -- 預期結果:5
SELECT safe_divide(10, 0); -- 預期結果:NULL,console 會有警告
用 RAISE 常見錯誤
忘記寫訊息等級。 如果你沒寫等級,PostgreSQL 會報錯。
錯誤寫法:
RAISE '沒有等級的訊息';
正確寫法:
RAISE NOTICE '有 NOTICE 等級的訊息';
參數錯誤。 如果你用了 %,記得要給對應數量的變數。
錯誤寫法:
RAISE NOTICE '參數範例 %';
正確寫法:
RAISE NOTICE '參數範例 %', '值';
打斷流程。 RAISE EXCEPTION 用太多會讓重要操作被中斷,要用得剛剛好。
實用小技巧
- 小心用
WHEN OTHERS。 能寫明確錯誤就寫,避免把該特別處理的錯誤都吃掉。 - 用
RAISE來 debug。 千萬不要讓錯誤沒被處理。 - 記得效能。 錯誤處理會有成本,特別是大程序。
如果你都做對了,你的程序就會超穩,連突發狀況都能撐住。PM 看到一定超級 proud of you!
GO TO FULL VERSION