我們來更深入玩玩 PL/pgSQL,開始更積極地用它吧。
程式區塊
PL/pgSQL 的程式區塊就是這語言的主幹啦。可以說它是我們 function、procedure 跟其他魔法的骨架。區塊負責執行邏輯、處理資料、錯誤管理,全部都包在同一個「容器」裡。
PL/pgSQL 的區塊有結構,主要分三個部分:
DECLARE:宣告變數(可選)。BEGIN ... END:主要執行區塊,邏輯都在這裡跑。EXCEPTION:錯誤處理(可選)。
如果你喜歡比喻:想像一下做菜的食譜。雖然食譜一開始會列材料,真正的魔法還是在烹飪過程裡。用 PL/pgSQL 的語言來說:
DECLARE— 就是材料清單(變數)。BEGIN ... END— 就是混合、煎、煮的地方。EXCEPTION— 就是如果燒焦了要怎麼辦的備案。
PL/pgSQL 區塊語法
先來看一下區塊的基本結構,就像「骨架」一樣。之後我們再加點肉(或你愛的素起司)——也就是實際的邏輯。
DO $$
DECLARE
-- 這裡宣告變數
student_count INT;
BEGIN
-- 這裡執行邏輯
SELECT COUNT(*) INTO student_count FROM students;
RAISE NOTICE '學生總數: %', student_count;
EXCEPTION
-- 這裡處理錯誤
WHEN OTHERS THEN
RAISE NOTICE '發生錯誤。';
END;
$$;
我們來一步一步拆解這個例子。
DECLARE— 這裡宣告我們的變數。超讚的是,PL/pgSQL 幾乎支援 PostgreSQL 所有的資料型態,從基本的INTEGER到很酷的 JSONB 都有。要宣告變數,只要寫名字、型態,還有需要的話可以給初始值。
範例:
DECLARE
student_name TEXT; -- 學生名字的變數
course_count INT := 0; -- 初始值設為 0
is_graduated BOOLEAN; -- 布林變數
注意,變數可以有初始值(像 course_count),也可以沒有。
BEGIN ... END— 主要執行區塊。
這部分負責執行主要邏輯。你可以:
- 執行 SQL 查詢(
SELECT、INSERT等等)。 - 操作資料。
- 用控制結構(
IF、LOOP等等)。 - 用
RAISE印出 debug 訊息。
範例:
BEGIN
SELECT COUNT(*) INTO student_count FROM students;
IF student_count > 0 THEN
RAISE NOTICE '我們有學生!';
ELSE
RAISE NOTICE '找不到學生。';
END IF;
END;
EXCEPTION— 錯誤處理(可選)。
如果執行區塊時出錯,EXCEPTION 區段可以讓你攔截錯誤,做點有用的事——像是印出訊息或執行備用程式碼。
範例:
BEGIN
SELECT COUNT(*) INTO student_count FROM non_existing_table; -- 錯誤!
EXCEPTION
WHEN OTHERS THEN
RAISE NOTICE '哎呀,出錯啦!';
END;
實戰範例:計算學生數量
現在我們把所有部分組合起來,做個實用的例子。寫一個 PL/pgSQL 區塊,計算 students 表裡的學生數,然後印出訊息。
DO $$
DECLARE
total_students INT; -- 存學生數的變數
BEGIN
-- 計算學生數
SELECT COUNT(*) INTO total_students FROM students;
-- 印出結果訊息
RAISE NOTICE '學生人數: %', total_students;
EXCEPTION
-- 處理可能的錯誤,例如表不存在
WHEN OTHERS THEN
RAISE NOTICE '計算學生數時發生錯誤。';
END;
$$;
執行這個區塊會在 console 印出訊息。例如:學生人數: 42。
變數使用小細節
來看幾個重要的點:
給變數賦值。 要把資料存進變數,可以用 SELECT INTO:
SELECT COUNT(*) INTO total_students FROM students;
變數初始化。 如果你宣告變數時沒給值,預設就是 NULL。
例如:
DECLARE
my_var INT; -- 預設值是 NULL
RECORD 型態的變數。 這是很萬用的變數型態,可以存一整行資料。範例:
DECLARE
student RECORD;
BEGIN
SELECT * INTO student FROM students WHERE id = 1;
RAISE NOTICE '學生名字: %, 年齡: %', student.name, student.age;
END;
範例:計算學生的課程數
來解個實用問題:算出某個學生修了幾門課,然後印出來。
DO $$
DECLARE
student_id INT := 1; -- 學生 ID
course_count INT; -- 課程數的變數
BEGIN
-- 計算課程數
SELECT COUNT(*) INTO course_count
FROM enrollments
WHERE student_id = student_id;
-- 印出訊息
RAISE NOTICE '學生 ID % 修了 % 門課。', student_id, course_count;
EXCEPTION
WHEN OTHERS THEN
RAISE NOTICE '處理學生 ID % 時發生錯誤', student_id;
END;
$$;
這個區塊很彈性:你可以改 student_id,查不同學生修了幾門課。
錯誤與避免方法
如果你覺得 PL/pgSQL 在你心裡像微波爐裡的小熱狗亂跳,這很正常。剛開始常常會遇到「經典」錯誤。這裡舉幾個例子:
沒宣告變數。 如果你忘了用 DECLARE 宣告變數,PL/pgSQL 會報錯說變數「不存在」。
用 NULL 當值。 如果變數宣告了但沒給值,預設就是 NULL。這可能會讓程式怪怪的。例如:
IF my_var = NULL THEN -- 不會動!
要用 IS NULL:
IF my_var IS NULL THEN
錯誤用法的 EXCEPTION 區段。 有時候工程師會攔所有錯誤(WHEN OTHERS),但沒寫要做什麼。這樣會把真正的問題藏起來。最好還是印出錯誤訊息:
RAISE NOTICE '錯誤: %', SQLERRM;
GO TO FULL VERSION