Oracle 提供兩種實作方式:原生動態 SQL (EXECUTE IMMEDIATE) 與 DBMS_SQL 套件。
CREATE OR REPLACE PROCEDURE proc_test (
p_table_name VARCHAR2,
p_field1 VARCHAR2, p_datatype1 VARCHAR2,
p_field2 VARCHAR2, p_datatype2 VARCHAR2
) AS
v_sql VARCHAR2(500);
BEGIN
v_sql := 'CREATE TABLE ' || p_table_name || ' (' ||
p_field1 || ' ' || p_datatype1 || ', ' ||
p_field2 || ' ' || p_datatype2 || ')';
EXECUTE IMMEDIATE v_sql; -- 直接執行 DDL
EXCEPTION WHEN OTHERS THEN NULL;
END;
測試:EXEC proc_test('dinya_test','id','NUMBER(8) NOT NULL','name','VARCHAR2(100)'); → 成功建表。
CREATE OR REPLACE PROCEDURE proc_insert (p_id NUMBER, p_name VARCHAR2) AS
v_sql VARCHAR2(500);
BEGIN
v_sql := 'INSERT INTO dinya_test VALUES (:1, :2)';
EXECUTE IMMEDIATE v_sql USING p_id, p_name;
END;
查詢回傳單值用 INTO + USING:
v_sql := 'SELECT COUNT(*) FROM table_name WHERE id = :id';
EXECUTE IMMEDIATE v_sql INTO v_count USING p_id;
需輸出參數時用 RETURNING INTO 子句。
v_cur := DBMS_SQL.OPEN_CURSOR 開啟游標DBMS_SQL.PARSE(v_cur, v_sql, DBMS_SQL.NATIVE) 解析DBMS_SQL.BIND_VARIABLE(v_cur, ':name', value) 綁定v_rows := DBMS_SQL.EXECUTE(v_cur) 執行DBMS_SQL.CLOSE_CURSOR(v_cur) 關閉(例外處理區也要關閉)CREATE OR REPLACE PROCEDURE proc_dbms_sql (
p_table_name VARCHAR2, p_f1 VARCHAR2, p_dt1 VARCHAR2,
p_f2 VARCHAR2, p_dt2 VARCHAR2
) AS
v_cur NUMBER; v_sql VARCHAR2(200); v_rows NUMBER;
BEGIN
v_cur := DBMS_SQL.OPEN_CURSOR;
v_sql := 'CREATE TABLE '||p_table_name||' ('||p_f1||' '||p_dt1||', '||p_f2||' '||p_dt2||')';
DBMS_SQL.PARSE(v_cur, v_sql, DBMS_SQL.NATIVE); -- PARSE 即執行 DDL
v_rows := DBMS_SQL.EXECUTE(v_cur); -- DDL 可省略 EXECUTE
DBMS_SQL.CLOSE_CURSOR(v_cur);
EXCEPTION WHEN OTHERS THEN
DBMS_SQL.CLOSE_CURSOR(v_cur); RAISE;
END;
關鍵差異:DDL 不可使用綁定變數,直接拼接字串;PARSE 階段即執行 DDL,EXECUTE 可省略。
CREATE OR REPLACE PROCEDURE proc_dbms_sql_update (p_id NUMBER, p_name VARCHAR2) AS
v_cur NUMBER; v_sql VARCHAR2(200); v_rows NUMBER;
BEGIN
v_cur := DBMS_SQL.OPEN_CURSOR;
v_sql := 'UPDATE dinya_test2 SET name = :p_name WHERE id = :p_id';
DBMS_SQL.PARSE(v_cur, v_sql, DBMS_SQL.NATIVE);
DBMS_SQL.BIND_VARIABLE(v_cur, ':p_name', p_name);
DBMS_SQL.BIND_VARIABLE(v_cur, ':p_id', p_id);
v_rows := DBMS_SQL.EXECUTE(v_cur);
DBMS_SQL.CLOSE_CURSOR(v_cur);
EXCEPTION WHEN OTHERS THEN
DBMS_SQL.CLOSE_CURSOR(v_cur); RAISE;
END;
測試:EXEC proc_dbms_sql_update(2, 'csdn_dinya'); → 第二筆 name 更新成功。
需額外:
DBMS_SQL.DEFINE_COLUMN 定義輸出欄位 → EXECUTE → FETCH_ROWS 迴圈 → COLUMN_VALUE 取值 → VARIABLE_VALUE 取輸出參數。
| 特性 | EXECUTE IMMEDIATE | DBMS_SQL |
|---|---|---|
| 語法簡潔度 | 高 | 較繁瑣 |
| DDL 支援 | 直接支援 | PARSE 階段執行,不可綁定變數 |
| 動態欄位/表名 | 字串拼接 | 字串拼接 |
| 未知欄位數查詢 | 不支援 | 支援 (DEFINE_COLUMN 動態) |
| 效能 | 較好 | 略高開銷 |
建議:簡單動態 DML/DDL 優先用 EXECUTE IMMEDIATE;需解析未知結構查詢或大量重複解析優化時用 DBMS_SQL。