函數與過程最大的區別在於必須有返回值,且可直接嵌入 SQL 語句(SELECT/WHERE/ORDER BY)調用,極大擴展了 SQL 的表達能力。本文系統講解函數語法、參數模式、三種調用方式、純度級別與實戰案例。
CREATE [OR REPLACE] FUNCTION func_name (
param_name [IN | OUT | IN OUT] [NOCOPY] datatype [DEFAULT expr],
...
) RETURN return_datatype
[AUTHID {DEFINER | CURRENT_USER}]
{IS | AS}
[PRAGMA AUTONOMOUS_TRANSACTION;]
DECLARE
-- 聲明區
BEGIN
-- 執行區
RETURN return_value; -- 必須有 RETURN
EXCEPTION
-- 異常處理
RETURN fallback_value;
END [func_name];
v_result := func_name(args);SELECT func_name(args) FROM dual; 或 WHERE func_name(col) > 100CREATE OR REPLACE FUNCTION datetime_fun RETURN VARCHAR2 AS
BEGIN
RETURN TO_CHAR(SYSDATE, 'YYYY-MM-DD HH24:MI:SS');
END;
-- 調用
BEGIN DBMS_OUTPUT.PUT_LINE(datetime_fun); END;
SELECT datetime_fun FROM dual;
CREATE OR REPLACE FUNCTION getinfo_fun (
p_eno EMP.EMPNO%TYPE,
p_job OUT EMP.JOB%TYPE
) RETURN VARCHAR2 AS
v_name EMP.ENAME%TYPE;
v_cnt NUMBER;
BEGIN
SELECT COUNT(*) INTO v_cnt FROM emp WHERE empno = p_eno;
IF v_cnt > 0 THEN
SELECT ename, job INTO v_name, p_job FROM emp WHERE empno = p_eno;
END IF;
RETURN v_name;
END;
-- 調用
DECLARE v_id EMP.EMPNO%TYPE := 7369; v_name EMP.ENAME%TYPE; v_job EMP.JOB%TYPE;
BEGIN
v_name := getinfo_fun(v_id, v_job);
DBMS_OUTPUT.PUT_LINE('編號:'||v_id||' 姓名:'||v_name||' 職位:'||v_job);
END;
CREATE OR REPLACE FUNCTION in_fun (
p_a VARCHAR2 DEFAULT 'Java開發',
p_b IN VARCHAR2 DEFAULT '好好學習'
) RETURN VARCHAR2 AS
BEGIN
RETURN p_b;
END;
-- 調用
DECLARE v_res VARCHAR2(100);
BEGIN
v_res := in_fun; -- 使用兩個默認值
DBMS_OUTPUT.PUT_LINE(v_res); -- 輸出: 好好學習
END;
CREATE OR REPLACE FUNCTION out_fun (
p_a OUT VARCHAR2,
p_b OUT VARCHAR2
) RETURN VARCHAR2 AS
BEGIN
p_a := 'Java實戰';
p_b := 'Oracle實戰';
RETURN p_b;
END;
-- 調用
DECLARE v_a VARCHAR2(100); v_b VARCHAR2(100); v_r VARCHAR2(100);
BEGIN
v_r := out_fun(v_a, v_b);
DBMS_OUTPUT.PUT_LINE('返回:'||v_r||' OUT_a:'||v_a||' OUT_b:'||v_b);
END;
CREATE OR REPLACE FUNCTION dept_insert_fun (
p_dno DEPT.DEPTNO%TYPE,
p_dname DEPT.DNAME%TYPE,
p_loc DEPT.LOC%TYPE
) RETURN NUMBER AS
v_cnt NUMBER;
BEGIN
SELECT COUNT(*) INTO v_cnt FROM dept WHERE deptno = p_dno;
IF v_cnt > 0 THEN RETURN -1; END IF;
INSERT INTO dept(deptno, dname, loc) VALUES (p_dno, p_dname, p_loc);
COMMIT;
RETURN 0;
END;
-- SQL 中調用
SELECT dept_insert_fun(99, 'AI', 'Shanghai') FROM dual;
-- PL/SQL 調用
DECLARE v_res NUMBER;
BEGIN
v_res := dept_insert_fun(98, 'ML', 'Beijing');
IF v_res = 0 THEN DBMS_OUTPUT.PUT_LINE('成功'); ELSE DBMS_OUTPUT.PUT_LINE('失敗'); END IF;
END;
函數在 SQL 中調用時,Oracle 要求聲明其副作用範圍(8i/9i 必需,10g+ 部分放寬):
PRAGMA RESTRICT_REFERENCES(func_name, WNDS [, WNPS] [, RNDS] [, RNPS]);
| 級別 | 含義 | SQL 調用要求 |
|---|---|---|
| WNDS | 不修改數據庫表(無 DML) | SELECT/WHERE/VALUES 中調用必需 |
| RNDS | 不讀取數據庫表(無 SELECT) | WHERE/VALUES 中調用必需 |
| WNPS | 不修改包變量 | 包函數在 SQL 中調用建議 |
| RNPS | 不讀取包變量 | 包函數在 SQL 中調用建議 |
VAR v_sal NUMBER;
CALL get_sal_fun(7369) INTO :v_sal;
PRINT v_sal;
| 場景 | 推薦 | 理由 |
|---|---|---|
| 需在 SQL 中使用 | FUNCTION | 過程不可用於 SQL |
| 返回單一標量值 | FUNCTION | 語義清晰 |
| 返回多個值/游標/執行 DML | PROCEDURE | OUT/IN OUT 更靈活 |
| 執行複雜業務流程、事務控制 | PROCEDURE | 過程天然支持事務邊界 |
WNDS(10g+ 可自動推斷,但顯式聲明更安全)。PRAGMA AUTONOMOUS_TRANSACTION 並明確文檔說明。DETERMINISTIC 關鍵字,啟用結果緩存優化。RESULT_CACHE(11g+)實現跨會話緩存。