DB2 SQL 預存程序(`CREATE PROCEDURE (SQL)`)功能強大,但語法選項繁多,官方文件鐵軌圖讓人望而卻步。本文將完整語法拆解為 13 個核心子句,逐一說明語意、限制與最佳實務,並附上可直接執行的「中位數薪資查詢」實戰範例,做你開發時的隨身語法導航儀。
CREATE PROCEDURE procedure-name
( [ IN | OUT | INOUT parameter-name data-type [, ...] ] )
[ SPECIFIC specific-name ]
[ DYNAMIC RESULT SETS integer ]
[ CONTAINS SQL | READS SQL DATA | MODIFIES SQL DATA ]
[ DETERMINISTIC | NOT DETERMINISTIC ]
[ CALLED ON NULL INPUT ]
[ INHERIT SPECIAL REGISTERS ]
[ OLD SAVEPOINT LEVEL | NEW SAVEPOINT LEVEL ]
LANGUAGE SQL
[ EXTERNAL ACTION | NO EXTERNAL ACTION ]
[ PARAMETER CCSID ASCII | UNICODE ]
SQL-procedure-body
| # | 子句 | 關鍵語意與限制 | 建議/預設 |
|---|---|---|---|
| 1 | procedure-name | 同一 Schema 下「名稱+參數個數」必須唯一,參數型別不同也視為重複。 | 採用動詞+名詞命名,如 GET_EMP_STATS |
| 2 | IN/OUT/INOUT parameter-name data-type | IN 輸入、OUT 輸出、INOUT 雙向。parameter-name 程序內唯一。不支援 LONG VARCHAR/VARGRAPHIC、DATALINK、REFERENCE、UDT。 | 明確指定 IN/OUT,避免預設 IN 造成誤解 |
| 3 | SPECIFIC specific-name | 唯一別名,用於 `DROP`、`COMMENT`、`GRANT`,不能用於 CALL。未指定時系統產生時間戳名稱。 | 強烈建議指定,如 GET_EMP_STATS_V1 |
| 4 | DYNAMIC RESULT SETS integer | 宣告程序可傳回的結果集最大數量(游標)。雖無 `RETURN` 語句,但可透過 `WITH RETURN` 游標回傳。 | 需回傳結果集時設 >=1,預設 0 |
| 5 | SQL 存取層級 | CONTAINS SQL:不讀寫資料;READS SQL DATA:唯讀;MODIFIES SQL DATA:可增刪改(預設)。 | 最小權限原則:能 READS 就別 MODIFIES |
| 6 | DETERMINISTIC / NOT DETERMINISTIC | 相同輸入必得相同輸出為 Deterministic,優化器可快取結果。含亂數、時間、序列則為 NOT DETERMINISTIC。 | 預設 NOT DETERMINISTIC,能標 DETERMINISTIC 盡量標 |
| 7 | CALLED ON NULL INPUT | 固定語法,表示無論輸入參數是否 NULL 皆可呼叫,NULL 檢查由程序內部邏輯處理。 | 固定寫上 |
| 8 | INHERIT SPECIAL REGISTERS | 繼承呼叫端的特殊暫存器(如 CURRENT DATE、CURRENT USER、PATH 等)。 | 預設繼承,建議保留 |
| 9 | Savepoint 層級 | OLD SAVEPOINT LEVEL(預設):程序內 savepoint 不對外可見;NEW SAVEPOINT LEVEL:建立新層級,程序內 savepoint 可被外部 `RELEASE/ROLLBACK TO`。 | 多為預設 |
| 10 | LANGUAGE SQL | 固定寫法,指定主體為 SQL PL。 | 必填 |
| 11 | 外部動作 | EXTERNAL ACTION(預設):可能執行非資料庫動作(寫檔、發 HTTP),優化器保守;NO EXTERNAL ACTION:純資料庫操作,優化器可激進優化。 | 純 SQL 操作請加 NO EXTERNAL ACTION |
| 12 | PARAMETER CCSID | 輸出字串編碼。Unicode 資料庫預設 UNICODE,其他預設 ASCII (CCSID 3)。 | 跨編碼環境才需調整 |
| 13 | SQL-procedure-body | 程序主體:單一或多條 SQL PL 陳述式,以 `BEGIN ... END` 包裹。 | 見下方實戰範例 |
場景:Sample 資料庫 `STAFF` 表,輸出中位數薪資(OUT 參數)與高於中位數員工清單(結果集)。
CREATE PROCEDURE MEDIAN_RESULT_SET
(
OUT medianSalary DOUBLE
)
RESULT SETS 1
LANGUAGE SQL
NO EXTERNAL ACTION
READS SQL DATA
BEGIN
-- 變數宣告
DECLARE v_numRecords INT DEFAULT 1;
DECLARE v_counter INT DEFAULT 0;
-- 游標 1:按薪資排序,用於算中位數
DECLARE c1 CURSOR FOR
SELECT CAST(salary AS DOUBLE)
FROM staff
ORDER BY salary;
-- 游標 2:回傳結果集,WITH RETURN TO CALLER 關鍵字
DECLARE c2 CURSOR WITH RETURN TO CALLER FOR
SELECT name, job, CAST(salary AS INTEGER)
FROM staff
WHERE salary > medianSalary
ORDER BY salary;
-- 例外處理:找不到資料時給預設值
DECLARE EXIT HANDLER FOR NOT FOUND
SET medianSalary = 6666;
SET medianSalary = 0;
SELECT COUNT(*) INTO v_numRecords FROM STAFF;
OPEN c1;
WHILE v_counter < (v_numRecords / 2 + 1) DO
FETCH c1 INTO medianSalary;
SET v_counter = v_counter + 1;
END WHILE;
CLOSE c1;
OPEN c2; -- 不關閉,直接回傳給呼叫端
END
DYNAMIC RESULT SETS 數值必須 ≥ 實際開啟的 `WITH RETURN` 游標數。收藏價值:這 13 條子句涵蓋 99% 開發場景。下次寫預存程序時,對著這張表逐項勾選、填寫,即可產出語法正確、語意清晰、效能友善的 DB2 SQL Procedure。