我們現在就像秘密特務一樣——我們的 function 跟 procedure 都在執行任務:處理資料、做計算,或是在資料庫裡搞點魔法。但如果哪裡出錯了怎麼辦?怎麼知道是哪個階段壞掉了這個「資料按摩浴缸」?這時候 日誌記錄跟錯誤處理 就派上用場啦。
還記得我們之前有聊過怎麼在 PostgreSQL 跟 PL/pgSQL 裡「溝通」跟寫 log 嗎:
RAISE NOTICE:很 chill、很朋友的語氣——「欸,這裡沒事啦,不過你可能想看一下」。RAISE WARNING:語氣大聲一點——「喂,這裡怪怪的,你要不要來瞄一下」。RAISE EXCEPTION:警報大響——「STOP!演算法出事啦!我們停下來,不然全炸了」。
這些等級各有用途,選對很重要。
訊息在 code 裡長這樣:
DO $$
BEGIN
-- NOTICE 等級(都正常,只是通知)
RAISE NOTICE '只是通知:開始處理資料';
-- WARNING 等級(有點可疑)
RAISE WARNING '警告:欄位的資料格式可能不正確';
-- EXCEPTION 等級(嚴重錯誤)
RAISE EXCEPTION '錯誤:輸入值不合法!';
END $$;
什麼時候用:
RAISE NOTICE— debug 跟一般資訊輸出。RAISE WARNING— 提醒有可能資料怪怪的。RAISE EXCEPTION— 發生嚴重錯誤,function 必須停下來。
用 RAISE EXCEPTION 處理錯誤
RAISE EXCEPTION 就是你的緊急煞車。如果出事了,你可以直接停掉 function,然後丟出錯誤訊息。
最基本的用法長這樣:
RAISE EXCEPTION '你的錯誤訊息';
但如果想讓訊息更有料,可以用變數:
DECLARE
input_value INTEGER;
BEGIN
input_value := NULL;
IF input_value IS NULL THEN
RAISE EXCEPTION '錯誤:輸入值是 NULL。預期要 INTEGER';
END IF;
END;
訊息格式化
你可以直接把變數塞進訊息裡:
DECLARE
var1 TEXT := '資料';
var2 INTEGER := 42;
BEGIN
RAISE EXCEPTION '處理 % 時發生錯誤,ID 是 %', var1, var2;
END;
輸出:處理 資料 時發生錯誤,ID 是 42。
範例:資料驗證
想像一下你有個 procedure 要收人的年齡。如果年齡是負的,當然要丟錯誤:
CREATE OR REPLACE FUNCTION validate_age(age INTEGER)
RETURNS VOID AS $$
BEGIN
IF age < 0 THEN
RAISE EXCEPTION '年齡不能是負的:%', age;
END IF;
END;
$$ LANGUAGE plpgsql;
-- 呼叫 function
SELECT validate_age(-5); -- 會丟錯誤
用 RAISE NOTICE 通知
如果 RAISE EXCEPTION 是警報,RAISE NOTICE 就像朋友拍拍你肩膀。這個等級可以加註解,讓你知道 function 裡發生什麼事。
什麼時候用 RAISE NOTICE:
- 輸出 debug 資訊(像是變數目前的值)。
- 告訴你某個階段開始或計算結果。
範例:資訊訊息
CREATE OR REPLACE FUNCTION calculate_discount(price NUMERIC, discount_rate NUMERIC)
RETURNS NUMERIC AS $$
DECLARE
final_price NUMERIC;
BEGIN
RAISE NOTICE '折扣前價格:%', price;
RAISE NOTICE '折扣率:%', discount_rate;
final_price := price - (price * discount_rate);
RAISE NOTICE '最終價格:%', final_price;
RETURN final_price;
END;
$$ LANGUAGE plpgsql;
-- 呼叫 function
SELECT calculate_discount(100, 0.2);
-- 會輸出:
-- NOTICE: 折扣前價格:100
-- NOTICE: 折扣率:0.2
-- NOTICE: 最終價格:80
實戰:規劃與日誌
假設你有個很複雜的資料處理 procedure,你想知道現在跑到哪個階段:
CREATE OR REPLACE FUNCTION process_data_step_by_step()
RETURNS VOID AS $$
BEGIN
RAISE NOTICE '步驟 1:準備資料';
-- 這裡寫第一階段的邏輯
RAISE NOTICE '步驟 2:驗證資料';
-- 這裡寫第二階段的邏輯
RAISE NOTICE '步驟 3:儲存資料';
-- 這裡寫第三階段的邏輯
END;
$$ LANGUAGE plpgsql;
-- 呼叫 function
SELECT process_data_step_by_step();
-- log 裡會看到每個步驟
再來一個例子。 想像有個商店,只有訂單金額超過某個數才有折扣:
CREATE OR REPLACE FUNCTION apply_discount(order_amount NUMERIC)
RETURNS NUMERIC AS $$
BEGIN
IF order_amount < 50 THEN
RAISE EXCEPTION '錯誤:訂單金額必須至少 50,現在是:%', order_amount;
END IF;
RETURN order_amount * 0.9; -- 給 10% 折扣
END;
$$ LANGUAGE plpgsql;
-- 呼叫 function
SELECT apply_discount(30); -- 錯誤:訂單金額必須至少 50
常見錯誤
錯誤 1: 記 log 沒帶參數。
這樣在大 procedure 裡根本看不懂:
RAISE NOTICE '發生錯誤'; -- 為什麼?哪裡?怎麼了?
建議: 一定要加 context:
RAISE NOTICE 'process_data() 發生錯誤:輸入值:%', input_value;
錯誤 2: 該用 RAISE WARNING 的地方卻用 RAISE EXCEPTION。
如果你太愛丟 exception,code 會一直被中斷,資料處理超麻煩。
小撇步: 日誌等級要用對。debug 用 NOTICE,真的嚴重才用 EXCEPTION。
錯誤 3: 完全沒記 log。
這就像在黑暗房間找鑰匙。沒 log,debug 複雜流程根本不可能。
小撇步: function 關鍵階段一定要加 RAISE NOTICE,尤其是大又複雜的 function。
GO TO FULL VERSION