首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 數據庫知識 >  >> Oracle 資料庫 >> Oracle 資料庫基礎

Oracle存儲過程動態SQL實戰:EXECUTE IMMEDIATE與REF CURSOR完全指南

     編輯:Oracle 資料庫基礎

兩大動態SQL引擎

特性EXECUTE IMMEDIATEDBMS_SQL
適用版本8i+ (主流)7.3+ (舊版兼容)
語法複雜度簡單、直觀繁瑣(OPEN/PARSE/BIND/EXECUTE/FETCH/CLOSE)
性能優(內部優化)略低
功能覆蓋95%場景100%(含描述未知列數/類型)
推薦度⭐⭐⭐⭐⭐⭐ (維護舊代碼)

場景一:DDL與DML動態執行


-- 匿名塊演示
BEGIN
  -- DDL: 動態建表
  EXECUTE IMMEDIATE 'DROP TABLE temp_1 PURGE';
  EXECUTE IMMEDIATE 'CREATE TABLE temp_1(name VARCHAR2(8))';

  -- DML: 參數化綁定 (防注入、計劃複用)
  DECLARE
    v_name    VARCHAR2(8) := '測試人員';
    v_addr    VARCHAR2(10) := '北京';
    v_sql     VARCHAR2(200) := 'INSERT INTO temp_1(name, address) VALUES (:1, :2)';
  BEGIN
    EXECUTE IMMEDIATE v_sql USING v_name, v_addr;
    COMMIT;
  END;
END;
/

場景二:返回單行結果 (INTO子句)


DECLARE
  v_sql     VARCHAR2(200) := 'SELECT * FROM temp_1 WHERE name = :b1 AND ROWNUM = 1';
  v_name    VARCHAR2(8) := '測試人員';
  v_rec     temp_1%ROWTYPE;
BEGIN
  EXECUTE IMMEDIATE v_sql INTO v_rec USING v_name;
  DBMS_OUTPUT.PUT_LINE('Found: ' || v_rec.name || ', ' || v_rec.address);
EXCEPTION
  WHEN NO_DATA_FOUND THEN
    DBMS_OUTPUT.PUT_LINE('No matching row');
END;
/

場景三:返回結果集 (REF CURSOR + 包裝)


-- 1. 定義包規範 (弱類型 REF CURSOR 才能動態綁定)
CREATE OR REPLACE PACKAGE pkg_dyn AS
  TYPE ref_cur IS REF CURSOR;  -- 弱類型,無RETURN子句
  FUNCTION get_data(p_id NUMBER) RETURN ref_cur;
END pkg_dyn;
/

-- 2. 包體實現
CREATE OR REPLACE PACKAGE BODY pkg_dyn AS
  FUNCTION get_data(p_id NUMBER) RETURN ref_cur IS
    v_rc   ref_cur;
    v_sql  VARCHAR2(500);
  BEGIN
    IF p_id = 0 THEN
      -- 靜態游標
      OPEN v_rc FOR SELECT id, name, address FROM temp_1;
    ELSE
      -- 動態SQL游標
      v_sql := 'SELECT id, name, address FROM temp_1 WHERE id = :b1';
      OPEN v_rc FOR v_sql USING p_id;
    END IF;
    RETURN v_rc;
  END get_data;
END pkg_dyn;
/

-- 3. 調用示例 (PL/SQL)
DECLARE
  v_rc   pkg_dyn.ref_cur;
  v_id   temp_1.id%TYPE;
  v_name temp_1.name%TYPE;
  v_addr temp_1.address%TYPE;
BEGIN
  v_rc := pkg_dyn.get_data(1);  -- 動態查詢
  LOOP
    FETCH v_rc INTO v_id, v_name, v_addr;
    EXIT WHEN v_rc%NOTFOUND;
    DBMS_OUTPUT.PUT_LINE(v_id || ':' || v_name || ':' || v_addr);
  END LOOP;
  CLOSE v_rc;
END;
/

關鍵最佳實踐

規則說明反例後果
綁定變量用 :1 :2 或 :b1 :b2位置/命名綁定,防注入、共享游標拼接字符串導致注入風險、硬解析風暴
REF CURSOR必須弱類型TYPE t IS REF CURSOR; 無RETURN強類型游標無法OPEN FOR dynamic_sql
動態SQL字符串不含終止分號'SELECT ...' 而非 'SELECT ...;'ORA-00911: invalid character
單行查詢必須保證唯一行加ROWNUM=1或主鍵條件TOO_MANY_ROWS異常
異常處理覆蓋 NO_DATA_FOUND單行查詢無結果時未捕獲導致調用者報錯

進階:DBMS_SQL 描述未知結構 (極少用)


-- 僅當列數/類型完全未知時使用(如通用導出工具)
DECLARE
  c       INTEGER := DBMS_SQL.OPEN_CURSOR;
  v_sql   VARCHAR2(500) := 'SELECT * FROM ' || p_table;
  col_cnt INTEGER;
  desc_t  DBMS_SQL.DESC_TAB;
BEGIN
  DBMS_SQL.PARSE(c, v_sql, DBMS_SQL.NATIVE);
  DBMS_SQL.DESCRIBE_COLUMNS(c, col_cnt, desc_t);
  -- 根據desc_t動態DEFINE_COLUMN並FETCH
  DBMS_SQL.CLOSE_CURSOR(c);
END;
架構決策:
• 99%動態SQL場景直接用 EXECUTE IMMEDIATE,代碼清晰、性能優、維護易。
• 僅在構建通用元數據工具(如自動導出/遷移/比對框架)時才考慮DBMS_SQL。
• 所有動態SQL嚴禁拼接業務參數,全量使用USING綁定變量。
• 批量DML優先FORALL + 集合綁定,而非循環EXECUTE IMMEDIATE。
• 動態DDL操作前後建議ALTER SYSTEM FLUSH SHARED_POOL避免字典緩存不一致。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved