首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫

Oracle PL/SQL 自定義函數大全:無參/入參/出參/入出參/結果緩存/記錄類型/集合類型/異常處理/編譯管理

     編輯:Oracle 資料庫相關知識

PL/SQL 函數是業務邏輯封裝、SQL 擴展、代碼復用的核心單元。本文系統覆蓋無參、IN、OUT、IN OUT、RESULT_CACHE、返回記錄類型 (%ROWTYPE)、返回集合類型、異常處理、編譯狀態管理、源碼查看 9 大類函數創建與調用實例,附性能優化與安全建議。

一、函數創建語法模板

CREATE OR REPLACE FUNCTION func_name
  (param1 IN type1, param2 OUT type2, param3 IN OUT type3)
  RETURN return_type
  [DETERMINISTIC] [PARALLEL_ENABLE] [RESULT_CACHE [RELIES_ON (table)]]
  [AUTHID DEFINER | CURRENT_USER]
IS
  -- 變量聲明
BEGIN
  -- 執行體
  RETURN value;
EXCEPTION
  WHEN exception_name THEN
    -- 異常處理
    RAISE_APPLICATION_ERROR(-20001, 'Custom message');
END;
/

二、9 大類函數實戰示例

1. 無參數函數:返回格式化當前時間

CREATE OR REPLACE FUNCTION cur_datetime RETURN VARCHAR2 IS
BEGIN
  RETURN TO_CHAR(SYSDATE, 'YYYY"年"MM"月"DD"日" HH24"時"MI"分"SS"秒"');
END;
/
-- 調用
BEGIN DBMS_OUTPUT.PUT_LINE(cur_datetime); END;
/
SELECT cur_datetime FROM dual;

2. IN 參數函數:按員工姓名查工資

CREATE OR REPLACE FUNCTION get_sal(p_name VARCHAR2) RETURN NUMBER IS
  v_sal emp.sal%TYPE;
BEGIN
  SELECT sal INTO v_sal FROM emp WHERE UPPER(ename) = UPPER(p_name);
  RETURN v_sal;
EXCEPTION
  WHEN NO_DATA_FOUND THEN
    RAISE_APPLICATION_ERROR(-20000, '員工不存在: ' || p_name);
END;
/
-- 調用
BEGIN DBMS_OUTPUT.PUT_LINE('工資:' || get_sal('SCOTT')); END;
/
VAR salary NUMBER
EXEC :salary := get_sal(name => 'SCOTT')  -- 命名參數傳遞
SELECT get_sal('SCOTT') FROM dual;

3. OUT 參數函數:返回員工姓名與職位

CREATE OR REPLACE FUNCTION get_info(p_eno NUMBER, p_title OUT VARCHAR2) RETURN VARCHAR2 IS
  v_name emp.ename%TYPE;
BEGIN
  SELECT ename, job INTO v_name, p_title FROM emp WHERE empno = p_eno;
  RETURN v_name;
EXCEPTION
  WHEN NO_DATA_FOUND THEN
    RAISE_APPLICATION_ERROR(-20000, '員工編號不存在: ' || p_eno);
END;
/
-- 調用 (PL/SQL 塊)
DECLARE v_name VARCHAR2(20); v_job VARCHAR2(20);
BEGIN
  v_name := get_info(7788, v_job);
  DBMS_OUTPUT.PUT_LINE('姓名:' || v_name || ', 崗位:' || v_job);
END;
/

4. IN OUT 參數函數:漲薪並返回新工資

CREATE OR REPLACE FUNCTION get_upd_info(p_eno NUMBER, p_sal_chg IN OUT NUMBER) RETURN VARCHAR2 IS
  v_name emp.ename%TYPE;
BEGIN
  UPDATE emp SET sal = sal + p_sal_chg WHERE empno = p_eno
  RETURNING ename, sal INTO v_name, p_sal_chg;
  RETURN v_name;
END;
/
-- 調用
VAR sal_chg NUMBER
EXEC :sal_chg := 200
VAR name VARCHAR2(20)
EXEC :name := get_upd_info(7788, :sal_chg)
PRINT name sal_chg

5. 結果緩存函數:11g+ 重複調用自動命中緩存

CREATE OR REPLACE FUNCTION get_name(p_no VARCHAR2) RETURN NUMBER
  RESULT_CACHE RELIES_ON(emp) IS
  v_name emp.ename%TYPE;
BEGIN
  SELECT ename INTO v_name FROM emp WHERE empno = p_no;
  RETURN v_name;
END;
/
-- 同一會話/跨會話重複調用相同參數,直接返回緩存結果,不再執行 SQL
SELECT get_name('7788') FROM dual;
  • RELIES_ON(emp):指定依賴表,表數據變更自動失效緩存。
  • 適用於**低頻變更、高頻調用**的查詢函數(如參數表查找、編碼轉換)。

6. 返回記錄類型 (%ROWTYPE)

CREATE OR REPLACE FUNCTION get_emp_rec(p_eno NUMBER) RETURN emp%ROWTYPE IS
  v_rec emp%ROWTYPE;
BEGIN
  SELECT * INTO v_rec FROM emp WHERE empno = p_eno;
  RETURN v_rec;
EXCEPTION
  WHEN NO_DATA_FOUND THEN
    RAISE_APPLICATION_ERROR(-20000, '員工不存在');
END;
/
-- 調用
DECLARE v_rec emp%ROWTYPE;
BEGIN
  v_rec := get_emp_rec(7788);
  DBMS_OUTPUT.PUT_LINE('姓名:' || v_rec.ename || ', 部門:' || v_rec.deptno);
END;
/

7. 返回集合類型 (Nested Table)

CREATE OR REPLACE TYPE ename_tab_type IS TABLE OF VARCHAR2(20);
/
CREATE OR REPLACE FUNCTION get_dept_names(p_dno NUMBER) RETURN ename_tab_type IS
  v_names ename_tab_type;
BEGIN
  SELECT ename BULK COLLECT INTO v_names FROM emp WHERE deptno = p_dno;
  RETURN v_names;
EXCEPTION
  WHEN NO_DATA_FOUND THEN
    RAISE_APPLICATION_ERROR(-20099, '部門不存在');
END;
/
-- 調用
DECLARE v_names ename_tab_type;
BEGIN
  v_names := get_dept_names(20);
  FOR i IN 1..v_names.COUNT LOOP
    DBMS_OUTPUT.PUT_LINE('姓名: ' || v_names(i));
  END LOOP;
END;
/

8. 異常處理最佳實踐

  • 業務異常用 RAISE_APPLICATION_ERROR(-20xxx, 'msg'),錯誤碼範圍 -20000 ~ -20999。
  • 系統異常 (NO_DATA_FOUND, TOO_MANY_ROWS, VALUE_ERROR) 統一捕獲轉業務異常。
  • 避免 WHEN OTHERS THEN NULL 吞噬異常,至少記錄日志 DBMS_OUTPUT / LOG_TABLE。

9. 編譯管理與源碼查看

-- 查看無效對象
SELECT object_name FROM user_objects WHERE status = 'INVALID' AND object_type = 'FUNCTION';

-- 重新編譯
ALTER FUNCTION get_info COMPILE;
ALTER FUNCTION get_info COMPILE BODY;  -- 僅編譯 Body

-- 查看源碼
SELECT text FROM user_source WHERE name = 'GET_INFO' ORDER BY line;

-- 刪除函數
DROP FUNCTION get_name;

三、參數傳遞三種方式對比

方式語法優點缺點
位置傳遞func(a, b, c)簡潔參數順序耦合,可讀性差
命名傳遞func(p1=>a, p2=>b)順序無關,自文檔化稍長
混合傳遞func(a, p2=>b)靈活命名參數必須在最後

四、性能與安全建議

  1. DETERMINISTIC:純函數(同輸入必同輸出、無副作用)加此關鍵字,優化器可在 SQL 中緩存結果(如 SELECT DETERMINISTIC_FUNC(x) FROM t)。
  2. PARALLEL_ENABLE:函數內無會話狀態、無 DML,可並行查詢中安全調用。
  3. RESULT_CACHE:11g+ 共享池級緩存,跨會話生效,需評估緩存命中率與失效開銷。
  4. SQL 注入防護:動態 SQL 必須用綁定變量 (USING) + DBMS_ASSERT。
  5. 權限最小化:GRANT EXECUTE ON func TO role,避免 GRANT EXECUTE ANY PROCEDURE。

將函數設計原則(單一職責、無副作用、參數模式精確、異常轉業務碼、緩存策略顯式)納入代碼審查清單,構建高性能、可維護、安全的 PL/SQL 函數庫。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved