當查詢語句在執行時才確定(如使用者自訂報表條件),需用動態 SQL 執行並回傳結果集。Oracle 提供兩種主流方式:DBMS_SQL 套件(底層游標控制)與SYS_REFCURSOR(弱型別游標變數)。本文完整示範從建表、PL/SQL 區塊驗證、建立預存程序到 SQL*Plus 測試的全流程。
CREATE TABLE AAA (AA NVARCHAR2(100), BB NVARCHAR2(100), CC NVARCHAR2(100), DD NVARCHAR2(100));
INSERT INTO AAA VALUES ('1','1','1','1');
DECLARE
V_CURSOR NUMBER;
V_STAT NUMBER;
AA, BB, CC, DD VARCHAR2(100);
V_SQL VARCHAR2(200) := 'SELECT * FROM AAA';
BEGIN
V_CURSOR := DBMS_SQL.OPEN_CURSOR; -- 1. 開啟游標
DBMS_SQL.PARSE(V_CURSOR, V_SQL, DBMS_SQL.NATIVE); -- 2. 解析 SQL
DBMS_SQL.DEFINE_COLUMN(V_CURSOR, 1, AA, 100); -- 3. 定義輸出欄位
DBMS_SQL.DEFINE_COLUMN(V_CURSOR, 2, BB, 100);
DBMS_SQL.DEFINE_COLUMN(V_CURSOR, 3, CC, 100);
DBMS_SQL.DEFINE_COLUMN(V_CURSOR, 4, DD, 100);
V_STAT := DBMS_SQL.EXECUTE(V_CURSOR); -- 4. 執行
LOOP
EXIT WHEN DBMS_SQL.FETCH_ROWS(V_CURSOR) <= 0; -- 5. 逐列提取
DBMS_SQL.COLUMN_VALUE(V_CURSOR, 1, AA);
DBMS_SQL.COLUMN_VALUE(V_CURSOR, 2, BB);
DBMS_SQL.COLUMN_VALUE(V_CURSOR, 3, CC);
DBMS_SQL.COLUMN_VALUE(V_CURSOR, 4, DD);
DBMS_OUTPUT.PUT_LINE(AA||';'||BB||';'||CC||';'||DD);
END LOOP;
DBMS_SQL.CLOSE_CURSOR(V_CURSOR); -- 6. 關閉游標
END;
/
-- 輸出:1;1;1;1
-- 1. 建立預存程序
CREATE OR REPLACE PROCEDURE MY_PROC (
MYSQLVAL IN VARCHAR2,
RESCUR OUT SYS_REFCURSOR
) AS
BEGIN
OPEN RESCUR FOR MYSQLVAL;
END;
/
-- 2. SQL*Plus 測試
VAR R REFCURSOR;
EXEC MY_PROC('SELECT * FROM AAA', :R);
PRINT R;
-- 輸出正常結果集
| 維度 | DBMS_SQL | SYS_REFCURSOR |
|---|---|---|
| 適用場景 | 欄位數/型別完全未知、需動態構建 SELECT 列表 | SQL 文本動態但結果集結構固定 |
| 程式碼複雜度 | 高(手動 DEFINE/COLUMN_VALUE) | 極低(OPEN FOR 即可) |
| 效能 | 略高開銷 | 較佳 |
| 客戶端支援 | 需特定 API 解析 | ODP.NET/JDBC 直接對應 ResultSet/RefCursor |
using Oracle.ManagedDataAccess.Client;
using System.Data;
public DataTable ExecuteDynamicQuery(string sql)
{
using (var conn = new OracleConnection(connStr))
using (var cmd = new OracleCommand("MY_PROC", conn) { CommandType = CommandType.StoredProcedure })
{
cmd.Parameters.Add("MYSQLVAL", OracleDbType.Varchar2, sql, ParameterDirection.Input);
cmd.Parameters.Add("RESCUR", OracleDbType.RefCursor, ParameterDirection.Output);
conn.Open();
using (var adapter = new OracleDataAdapter(cmd))
{
var dt = new DataTable();
adapter.Fill(dt);
return dt;
}
}
}
DBMS_ASSERT 套件驗證物件名稱:DBMS_ASSERT.ENQUOTE_NAME(user_input)優先使用 SYS_REFCURSOR 滿足 95% 動態查詢需求,僅在極度動態結構時啟用 DBMS_SQL。