PL/SQL 是 Oracle 資料庫的核心程式語言,但許多專案因忽視基本原則而陷入維護困境。本文整理 14 種常見的「最差實踐」,分析其危害並提供具體重構方案,助你編寫健壯、高效、可維護的程式碼。
Shared Pool 載入壓力大(UNIX 64K/桌面 32K 為單位),超過 5,000 行即觸發除錯問題;邏輯耦合嚴重,變更風險極高。
USER_OBJECT_SIZE.PARSED_SIZE 追蹤物件大小。CREATE OR REPLACE PACKAGE BODY pkg_bad IS
g_global_var NUMBER;
PROCEDURE proc_a IS BEGIN g_global_var := 1; END;
PROCEDURE proc_b IS BEGIN g_global_var := 2; END; -- 遠端修改
END;
Package Specification 中宣告的變數對所有 Session 可見,導致狀態不可預測、除錯極難、併發安全風險極高。
GET/SET 函數,集中控制存取邏輯與驗證。PROCEDURE proc_a IS BEGIN UPDATE t SET col = 10; END;
PROCEDURE proc_b IS BEGIN DELETE FROM t WHERE col = 10; END;
同一張表的存取邏輯分散在多處,SQL 調優困難,結構變更影響面大。
pkg_t_api.ins_t、pkg_t_api.del_t 封裝所有 DML。EXCEPTION
WHEN OTHERS THEN
log_error(SQLCODE, SQLERRM); -- 丟失錯誤發生位置與類型
END;
無法區分 NO_DATA_FOUND、DUP_VAL_ON_INDEX 等具體錯誤,掩蓋真實問題,導致資料不一致難以追蹤。
DECLARE e_biz_error EXCEPTION; PRAGMA EXCEPTION_INIT(e_biz_error, -20001);)。v_name VARCHAR2(30); -- 表欄位實際為 VARCHAR2(50)
結構變更時需同步修改所有程式碼,極易遺漏導致 VALUE_ERROR。
全面使用 %TYPE(單欄位)與 %ROWTYPE(整列)錨定定義:
v_name employees.last_name%TYPE;
r_emp employees%ROWTYPE;
IF v_status = '1' THEN ... -- '1' 代表什麼?
pkg_constants)集中管理:c_status_active CONSTANT VARCHAR2(1) := '1';pkg_constants.is_active(p_status)。併行開發覆蓋代碼、無歷史追溯、部署版本不一致。
PL/SQL Developer VCS、Gitora 等工具實現 IDE 級版控整合。TYPE ... UNDER),將差異邏輯封裝在子型別方法中,對外統一介面。SAVEPOINT a; UPDATE ...; COMMIT; DELETE ...; ROLLBACK TO a;
破壞交易邊界,導致資料不一致、死鎖、邏輯極難追蹤。這是危害最大的反模式。
PRAGMA AUTONOMOUS_TRANSACTION。SQL 硬解析風暴,Shared Pool 暴增,執行計劃無法複用,效能崩潰。
所有動態/靜態 SQL 均使用繫結變數:
EXECUTE IMMEDIATE 'SELECT * FROM t WHERE id = :b1' INTO v USING p_id;
ROWNUM 產生於 ORDER BY 之前,結果具隨機性,極易導致業務邏輯錯誤且難以重現。
FETCH FIRST 1 ROW ONLY (12c+) 或 MAX/MIN、ROW_NUMBER() OVER 確定性取值。ORDER BY 確保穩定。失去編譯期檢查、SQL Injection 風險、執行計劃無法預測、除錯困難。
DBMS_ASSERT 防注入。ROWID 為物理存儲地址,隨 MOVE、EXP/IMP、分區維護等操作改變,業務邏輯不應依賴底層結構。
永遠使用 主鍵 或 唯一鍵 進行資料定位與 DML 操作。
| # | 反模式 | 核心對策 |
|---|---|---|
| 1 | 超長程式碼 | 拆包、模組化、持久化匿名區塊 |
| 2 | 失控全域變數 | 參數傳遞、GET/SET 封裝 |
| 3 | SQL 硬編碼分散 | 資料存取層 API 化 |
| 4 | WHEN OTHERS 吞噬例外 | 自訂例外、分層處理、豐富錯誤上下文 |
| 5 | 型別長度硬編碼 | %TYPE / %ROWTYPE 錨定 |
| 6 | 無單元測試 | utPLSQL、CI/CD 整合 |
| 7 | 魔術數值硬編碼 | 常數 Package 集中管理 |
| 8 | 無版控管理 | Git 為真實來源、自動化部署 |
| 9 | IF-ELSE 地獄 | 策略模式/查表法/重構函數 |
| 10 | 手動交易控制 | 交易上移、自治交易僅用於日誌 |
| 11 | 無繫結變數 | 全面參數化 SQL |
| 12 | ROWNUM=1 隨機性 | 確定性排序取值 |
| 13 | 濫用動態 SQL | 靜態優先、中間層承擔變化 |
| 14 | 直接用 ROWID | 主鍵/唯一鍵定位 |