-- 僅輸入
CREATE OR REPLACE PROCEDURE proc1 IS
BEGIN
INSERT INTO stu_info VALUES (1, 'liheng');
END;
-- 輸入參數
CREATE OR REPLACE PROCEDURE proc2 (
v_id IN INT,
v_name IN VARCHAR2
) IS
BEGIN
INSERT INTO stu_info VALUES (v_id, v_name);
COMMIT;
END;
-- 輸出參數:使用 %TYPE 錚定表列類型,避免硬編碼
CREATE OR REPLACE PROCEDURE proc3 (
v_id IN INT,
v_name OUT VARCHAR2
) IS
v_var_name stu_info.stu_name%TYPE;
BEGIN
SELECT stu_name INTO v_var_name FROM stu_info WHERE stu_id = v_id;
v_name := v_var_name;
END;
%TYPE:錚定列類型,DDL 變更自動適配。SELECT ... INTO:單行查詢,需處理 NO_DATA_FOUND、TOO_MANY_ROWS。CREATE OR REPLACE PACKAGE pkg_stuinfo IS
TYPE stu_cur_type IS REF CURSOR;
PROCEDURE get_stu_info (p_cur OUT stu_cur_type);
END pkg_stuinfo;
/
CREATE OR REPLACE PACKAGE BODY pkg_stuinfo IS
PROCEDURE get_stu_info (p_cur OUT stu_cur_type) IS
v_cur stu_cur_type;
BEGIN
OPEN v_cur FOR SELECT * FROM stu_info;
p_cur := v_cur;
END;
END pkg_stuinfo;
/
CREATE OR REPLACE PACKAGE pkg_stuinfo IS
TYPE stu_cur_type IS REF CURSOR;
PROCEDURE get_stu_info (p_id IN INT, p_cur OUT stu_cur_type);
END pkg_stuinfo;
/
CREATE OR REPLACE PACKAGE BODY pkg_stuinfo IS
PROCEDURE get_stu_info (p_id IN INT, p_cur OUT stu_cur_type) IS
v_cur stu_cur_type;
BEGIN
IF p_id = 0 THEN
OPEN v_cur FOR SELECT * FROM stu_info;
ELSE
OPEN v_cur FOR SELECT * FROM stu_info WHERE stu_id = p_id;
END IF;
p_cur := v_cur;
END;
END pkg_stuinfo;
/
CREATE OR REPLACE PACKAGE pkg_stuinfo IS
TYPE stu_cur_type IS REF CURSOR;
PROCEDURE get_stu_info (p_name IN VARCHAR2, p_cur OUT stu_cur_type);
END pkg_stuinfo;
/
CREATE OR REPLACE PACKAGE BODY pkg_stuinfo IS
PROCEDURE get_stu_info (p_name IN VARCHAR2, p_cur OUT stu_cur_type) IS
v_cur stu_cur_type;
BEGIN
IF p_name IS NULL OR p_name = ' ' THEN
OPEN v_cur FOR SELECT * FROM stu_info;
ELSE
OPEN v_cur FOR SELECT * FROM stu_info WHERE stu_name LIKE '%' || p_name || '%';
END IF;
p_cur := v_cur;
END;
END pkg_stuinfo;
/
-- 匿名塊調用
DECLARE
c SYS_REFCURSOR;
v_id stu_info.stu_id%TYPE;
v_name stu_info.stu_name%TYPE;
BEGIN
pkg_stuinfo.get_stu_info('li', c);
LOOP
FETCH c INTO v_id, v_name;
EXIT WHEN c%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(v_id || ' - ' || v_name);
END LOOP;
CLOSE c;
END;
/
-- Java/JDBC 調用(概念)
-- CallableStatement cs = conn.prepareCall("{call pkg_stuinfo.get_stu_info(?, ?)}");
-- cs.setString(1, "li");
-- cs.registerOutParameter(2, OracleTypes.CURSOR);
-- cs.execute();
-- ResultSet rs = (ResultSet) cs.getObject(2);
| 維度 | 建議 |
|---|---|
| 參數類型 | 優先使用 %TYPE / %ROWTYPE 錚定,減少硬編碼維護成本 |
| 結果集返回 | 統一使用 SYS_REFCURSOR(弱類型遊標),Package 定義強類型 REF CURSOR 供內部使用 |
| 動態查詢條件 | 在 PL/SQL 中構建動態 SQL(OPEN cur FOR 'SELECT ...' USING ...)或條件分支 IF/ELSE 開啟不同遊標 |
| 異常處理 | SELECT INTO 必須包含 EXCEPTION WHEN NO_DATA_FOUND THEN ... WHEN TOO_MANY_ROWS THEN ... |
| 命名規範 | 參數名 不可與表列名相同(如 p_id vs id),否則引用歧義導致邏輯錯誤 |
| 事務控制 | Package 過程不 COMMIT,由調用方(Service 層)統一管理事務邊界 |
Oracle 存儲過程的進階應用核心在於:強類型參數錚定、REF CURSOR 統一結果集介面、Package 模塊化封裝、動態條件分支、完善異常處理。掌握這些模式,即可構建出既高性能又易維護的數據庫程式碼庫,完美支撐 Java/.NET/Python 等上層應用的數據訪問需求。