進入主題之前,先來看看你最有可能遇到的常見錯誤和小疏忽。畢竟 SQL 出錯真的很煩,而且通常都在最不想出錯的時候出現。
1. 語法錯誤:「忘記關閉 IF」
語法錯誤是最基本的問題,但比你想像的還常見。比如你忘了用 END IF; 關閉 IF 條件區塊,compiler 馬上就會給你臉色看。
錯誤範例:
CREATE OR REPLACE FUNCTION check_number(num INTEGER)
RETURNS TEXT AS $$
BEGIN
IF num > 0 THEN
RETURN 'Positive';
ELSE
RETURN 'Negative';
-- somewhere lost END IF;
END;
$$ LANGUAGE plpgsql;
你執行這段 code 會看到錯誤:ERROR: syntax error at or near "END"。為什麼?因為 IF 區塊沒關閉啦。
怎麼避免這種錯?
一定要養成寫 code 有結構的習慣。你開一個區塊(像 IF),馬上就把結束也寫好。來看正確範例:
CREATE OR REPLACE FUNCTION check_number(num INTEGER)
RETURNS TEXT AS $$
BEGIN
IF num > 0 THEN
RETURN 'Positive';
ELSE
RETURN 'Negative';
END IF; -- 記得關閉區塊
END;
$$ LANGUAGE plpgsql;
2. CASE 沒處理所有情況:「如果都沒中怎麼辦?」
用 CASE 時,一定要加 ELSE 處理意外狀況。沒這個分支可能會讓你拿到 NULL,然後 debug 到懷疑人生。
錯誤範例:
CREATE OR REPLACE FUNCTION grade_result(grade CHAR)
RETURNS TEXT AS $$
BEGIN
RETURN CASE grade
WHEN 'A' THEN 'Excellent'
WHEN 'B' THEN 'Good'
WHEN 'C' THEN 'Average'
-- 如果 grade = 'D' 或其他分數呢?
END;
END;
$$ LANGUAGE plpgsql;
如果你傳進去 D,function 會回傳 NULL,這可能會讓你的 code 出問題。
修正版:
CREATE OR REPLACE FUNCTION grade_result(grade CHAR)
RETURNS TEXT AS $$
BEGIN
RETURN CASE grade
WHEN 'A' THEN 'Excellent'
WHEN 'B' THEN 'Good'
WHEN 'C' THEN 'Average'
ELSE '未知分數' -- 把其他情況都抓起來
END;
END;
$$ LANGUAGE plpgsql;
3. 無窮迴圈的問題:「為什麼 server 卡住了?」
用 LOOP 很容易忘記寫跳出條件,這樣會讓你的 code 一直跑下去:
錯誤範例:
CREATE OR REPLACE FUNCTION infinite_loop_demo()
RETURNS VOID AS $$
DECLARE
i INTEGER := 1;
BEGIN
LOOP
i := i + 1;
-- 沒有跳出條件!
END LOOP;
END;
$$ LANGUAGE plpgsql;
這段 code 會讓 server 卡死,因為迴圈永遠不會結束。
怎麼修:
加個 EXIT 跳出條件就好:
CREATE OR REPLACE FUNCTION finite_loop_demo()
RETURNS VOID AS $$
DECLARE
i INTEGER := 1;
BEGIN
LOOP
i := i + 1;
IF i > 10 THEN
EXIT; -- 跳出條件
END IF;
END LOOP;
END;
$$ LANGUAGE plpgsql;
4. 迴圈漏掉資料:「那漏掉的資料怎麼辦?」
用 CONTINUE 跳過某些迴圈時,如果沒想清楚所有情況,可能會出錯。例如:
錯誤範例:
CREATE OR REPLACE FUNCTION skip_even()
RETURNS VOID AS $$
DECLARE
i INTEGER := 0;
BEGIN
WHILE i < 10 LOOP
i := i + 1;
IF i % 2 = 0 THEN
CONTINUE; -- 直接跳過偶數
END IF;
RAISE NOTICE '奇數: %', i;
END LOOP;
END;
$$ LANGUAGE plpgsql;
如果全部都是偶數呢?server 會跑,但你什麼都看不到。
怎麼修:
記得把所有資料都處理好,加點 log 方便追蹤:
CREATE OR REPLACE FUNCTION skip_even_logging()
RETURNS VOID AS $$
DECLARE
i INTEGER := 0;
BEGIN
WHILE i < 10 LOOP
i := i + 1;
IF i % 2 = 0 THEN
RAISE NOTICE '跳過偶數: %', i;
CONTINUE;
END IF;
RAISE NOTICE '奇數: %', i;
END LOOP;
END;
$$ LANGUAGE plpgsql;
這樣你就知道哪些數字被跳過了。
5. 錯誤處理寫錯:「我的 RAISE EXCEPTION 呢?」
用 RAISE EXCEPTION 處理錯誤很強大,但寫錯語法就 GG 了。
錯誤範例:
CREATE OR REPLACE FUNCTION calculate_square(num INTEGER)
RETURNS INTEGER AS $$
BEGIN
IF num < 0 THEN
RAISE '不允許負數!';
END IF;
RETURN num * num;
END;
$$ LANGUAGE plpgsql;
這段 code 會出錯,因為 RAISE 少了訊息等級(要加 EXCEPTION)。
修正版:
CREATE OR REPLACE FUNCTION calculate_square(num INTEGER)
RETURNS INTEGER AS $$
BEGIN
IF num < 0 THEN
RAISE EXCEPTION '不允許負數!';
END IF;
RETURN num * num;
END;
$$ LANGUAGE plpgsql;
6. log 寫錯:「為什麼我的錯誤沒寫進 error_log?」
寫 error_log table 時,如果 INSERT INTO 寫錯欄位名就會出錯。
錯誤範例:
CREATE OR REPLACE FUNCTION log_error(err_msg TEXT)
RETURNS VOID AS $$
BEGIN
INSERT INTO error_log (error_message, error_time)
VALUES (err_msg, CURRENT_TIMESTAMP); -- 如果欄位名改了呢?
END;
$$ LANGUAGE plpgsql;
如果 error_log table 的欄位名被改成 error_msg,這就會出錯。
怎麼避免:
一定要確認 table 結構,或是用嚴格 schema 管理資料。
7. 一般粗心和「人為疏失」
錯誤不只技術問題,還有粗心造成的。像是忘了 debug、不用的變數、沒排版,這些都會讓你的 function 變成一團亂。
範例:
DECLARE
i INTEGER; -- 幹嘛宣告不用的變數?
修正:把沒用的 code 刪掉,讓 code 乾淨又好懂。
現在你已經準備好避開 PL/pgSQL 最常見的錯誤,寫出讓你開心、bug 少的 code。記得多測試、多 log、bug 早點修——這樣你就能省下很多神經和客戶電話啦!
GO TO FULL VERSION