在 PostgreSQL 裡,函式是一個超強大的工具,可以自動化工作、寫 business logic,還能讓 server 更聰明。你可以把函式想像成在資料庫裡跑的小程式。它們很適合:
- 重複利用程式碼。 如果你常常寫一樣的查詢,把它包成函式,想用就 call 一下。
- 自動化任務。 比方說你要根據員工工時算薪水,用函式就超方便。
- 封裝邏輯。 把複雜的計算丟給 server 處理,client 端不用煩 SQL 查詢。
CREATE FUNCTION 的基本語法
建立函式的基本結構長這樣:
CREATE FUNCTION function_name(parameters) RETURNS return_type AS $$
BEGIN
-- 函式主體(邏輯)
RETURN 結果;
END;
$$ LANGUAGE plpgsql;
來拆解一下重點:
CREATE FUNCTION function_name(parameters):
這行是設定函式名稱 function_name,還有參數(如果有的話)。
參數可以有名字跟型別:my_param INTEGER、another_param TEXT。
RETURNS return_type:
這裡要指定函式會回傳什麼:單一值(INTEGER、TEXT 等等)或資料集(TABLE、RECORD)。
BEGIN ... END:
這兩個關鍵字包住的就是「主體」,所有魔法都在這裡發生。
RETURN 結果:
回傳函式執行的結果。要注意:回傳型別要跟 RETURNS 指定的一樣。
LANGUAGE plpgsql:
這裡指定用 PL/pgSQL 語言。PostgreSQL 也支援其他語言,但我們現在就用這個。
簡單範例:兩個數字相加
來寫一個回傳兩個整數和的函式。
CREATE FUNCTION add_numbers(a INT, b INT) RETURNS INT AS $$
BEGIN
RETURN a + b;
END;
$$ LANGUAGE plpgsql;
現在來呼叫它:
SELECT add_numbers(5, 7); -- 結果:12
這裡發生什麼事?
- 函式接收兩個參數
a跟b,型別都是INT。 - 函式裡直接把它們加起來(
a + b)然後回傳。 - 超簡單,就像計算機一樣!
用變數的範例
假設我們有一個大學資料庫,想知道有多少學生註冊。
來寫個函式:
CREATE FUNCTION count_students() RETURNS INT AS $$
DECLARE
total INT; -- 宣告一個變數來存結果
BEGIN
SELECT COUNT(*) INTO total FROM students; -- 算表格裡有幾列
RETURN total; -- 回傳結果
END;
$$ LANGUAGE plpgsql;
呼叫函式:
SELECT count_students(); -- 假設結果:120
這裡我們看到:
- 用變數
total來存 SQL 查詢的結果。 SELECT ... INTO指令會把查詢結果存進變數。
這種寫法很方便,如果你要先處理資料再回傳。
回傳多個值:RETURNS TABLE
剛剛的例子只回傳一個值。如果我們要回傳一組資料,比如學生清單?這時 RETURNS TABLE 就超好用。
範例:
CREATE FUNCTION get_students() RETURNS TABLE(id INT, name TEXT) AS $$
BEGIN
RETURN QUERY SELECT id, name FROM students;
END;
$$ LANGUAGE plpgsql;
呼叫函式:
SELECT * FROM get_students();
可能的結果:
| id | name |
|---|---|
| 1 | Alice |
| 2 | Bob |
| 3 | Charlie |
RETURN QUERY 的好處:直接在函式裡查詢
RETURN QUERY 讓我們可以直接把 SQL 查詢的結果回傳,省去中間步驟,函式更簡潔。
來寫個只回傳 active 學生的函式:
CREATE FUNCTION get_active_students() RETURNS TABLE(id INT, name TEXT) AS $$
BEGIN
RETURN QUERY SELECT id, name FROM students WHERE active = TRUE;
END;
$$ LANGUAGE plpgsql;
在呼叫 get_active_students() 之前,要先建立 students 表格並塞一些測試資料。可以這樣做:
-- 建立學生表格
CREATE TABLE students (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
active BOOLEAN DEFAULT TRUE
);
-- 加幾筆資料
INSERT INTO students (name, active) VALUES
('Alice', FALSE),
('Bob', TRUE),
('Charlie', TRUE),
('Dana', FALSE);
表格:
| id | name | active |
|---|---|---|
| 1 | Alice | false |
| 2 | Bob | true |
| 3 | Charlie | true |
| 4 | Dana | false |
現在呼叫:
SELECT * FROM get_active_students();
結果:
| id | name |
|---|---|
| 2 | Bob |
| 3 | Charlie |
執行前檢查資料正確性
函式可以用 IF 來檢查資料正不正確。比如我們可以寫一個函式,只有學生通過所有考試才讓他升級。
範例:
CREATE FUNCTION promote_student(student_id INT) RETURNS TEXT AS $$
DECLARE
passed_exams INT;
BEGIN
-- 算學生通過的考試數量
SELECT COUNT(*) INTO passed_exams
FROM exams
WHERE student_id = promote_student.student_id AND status = 'passed';
-- 檢查條件
IF passed_exams < 5 THEN
RETURN '學生通過的考試不夠多';
END IF;
-- 更新學生的課程
UPDATE students
SET course = course + 1
WHERE id = promote_student.student_id;
RETURN '學生升級了!';
END;
$$ LANGUAGE plpgsql;
寫函式時常見錯誤
沒寫回傳型別。 PostgreSQL 一定要你指定函式會回傳什麼。例如:
CREATE FUNCTION fail() AS $$ -- 錯誤:沒有 RETURNS
BEGIN
RETURN 1;
END;
$$ LANGUAGE plpgsql;
修正:
CREATE FUNCTION succeed() RETURNS INT AS $$
BEGIN
RETURN 1;
END;
$$ LANGUAGE plpgsql;
回傳型別不符。 如果你寫 RETURNS INT,就一定要回傳數字。回傳字串會出錯。
函式裡的 SQL 查詢寫錯。 一定要先測試查詢,才放進函式。最好先在 psql 或 pgAdmin 手動跑過。
GO TO FULL VERSION