動態 SQL 允許在運行時構建並執行 SQL 字符串,是實現通用查詢、動態表名、可選條件構建的核心技術。本文對比 Oracle (EXECUTE IMMEDIATE)、SQL Server (sp_executesql)、DB2 (PREPARE/EXECUTE) 三種語法差異,提供**帶參數綁定、帶返回游標、純執行**三種場景的標準模板代碼。
CREATE OR REPLACE PROCEDURE dyn_sql_demo AS
v_sql VARCHAR2(2000);
v_a VARCHAR2(20);
v_b VARCHAR2(20);
v_c VARCHAR2(20) := 'f';
v_d VARCHAR2(20) := 'g';
BEGIN
v_sql := 'SELECT MAX(a), MAX(b) FROM t1 WHERE c = :b1 OR c = :b2';
EXECUTE IMMEDIATE v_sql INTO v_a, v_b USING v_c, v_d;
-- 無 INTO 則無需 USING
END;
/
CREATE OR REPLACE PROCEDURE dyn_cursor (p_cursor OUT SYS_REFCURSOR) AS
v_sql VARCHAR2(2000);
v_c VARCHAR2(20) := 'f';
v_d VARCHAR2(20) := 'g';
BEGIN
v_sql := 'SELECT * FROM t1 WHERE c = :b1 OR c = :b2';
OPEN p_cursor FOR v_sql USING v_c, v_d;
END;
/
關鍵點:佔位符用 :b1, :b2 或 :name;USING 子句按位置綁定;游標由調用方 FETCH 關閉。
CREATE PROCEDURE dyn_sql_demo AS
DECLARE @sql NVARCHAR(2000); -- 必須 NVARCHAR
DECLARE @a VARCHAR(20), @b VARCHAR(20);
DECLARE @c VARCHAR(20) = 'f', @d VARCHAR(20) = 'g';
BEGIN
SET @sql = N'SELECT @outA = MAX(a), @outB = MAX(b) FROM t1 WHERE c = @inC OR c = @inD';
EXECUTE sp_executesql @sql
, N'@outA VARCHAR(20) OUT, @outB VARCHAR(20) OUT, @inC VARCHAR(20), @inD VARCHAR(20)' -- 參數定義 (單行!)
, @a OUT, @b OUT, @c, @d; -- 參數值按定義順序
END
GO
SET @sql = N'SELECT * FROM t1 WHERE c = @inC OR c = @inD';
EXECUTE sp_executesql @sql, N'@inC VARCHAR(20), @inD VARCHAR(20)', @c, @d;
-- 直接返回結果集給調用者
關鍵點:@sql 必須 NVARCHAR;參數定義字符串 **必須單行寫完**;輸出參數在定義和傳值處均標 OUT;EXECUTE 後不能拼接字符串。
CREATE PROCEDURE dyn_sql_demo (IN v_c VARCHAR(20))
BEGIN
DECLARE v_sql VARCHAR(2000);
DECLARE v_a, v_b, v_d VARCHAR(20);
DECLARE c1 CURSOR FOR s1; -- s1 為 prepared statement 名
SET v_c = 'f'; SET v_d = 'g';
SET v_sql = 'SELECT MAX(a), MAX(b) FROM t1 WHERE c = ? OR c = ?'; -- 佔位符用 ?
PREPARE s1 FROM v_sql;
OPEN c1 USING v_c, v_d; -- 綁定參數
FETCH c1 INTO v_a, v_b;
CLOSE c1;
END
CREATE PROCEDURE dyn_cursor (IN v_c VARCHAR(20))
DYNAMIC RESULT SETS 1 LANGUAGE SQL
BEGIN
DECLARE v_sql VARCHAR(2000);
DECLARE v_d VARCHAR(20);
DECLARE c1 CURSOR WITH RETURN FOR s1; -- WITH RETURN 關鍵字
SET v_c = 'f'; SET v_d = 'g';
SET v_sql = 'SELECT * FROM t1 WHERE c = ? OR c = ?';
PREPARE s1 FROM v_sql;
OPEN c1 USING v_c, v_d; -- 打開即返回結果集
END
關鍵點:佔位符統一用 ?;PREPARE stmt FROM sql_string;游標聲明引用 stmt;OPEN ... USING 綁定;返回游標需在 CREATE PROCEDURE 聲明 DYNAMIC RESULT SETS 1 且游標用 WITH RETURN。
| 特性 | Oracle | SQL Server | DB2 |
|---|---|---|---|
| 動態 SQL 關鍵字 | EXECUTE IMMEDIATE | sp_executesql | PREPARE / EXECUTE |
| 佔位符語法 | :name 或 :b1 | @param_name | ? |
| SQL 字符串類型 | VARCHAR2 | NVARCHAR (必須) | VARCHAR |
| 參數綁定方式 | USING val1, val2 | 參數定義字符串 + 值列表 | OPEN cursor USING val1, val2 |
| 返回單行值 | INTO var1, var2 | 參數定義 OUT + 列表 OUT | FETCH cursor INTO var1, var2 |
| 返回結果集/游標 | OPEN cursor FOR sql USING | 直接 EXECUTE sp_executesql | DYNAMIC RESULT SETS + WITH RETURN |
| 註釋 | 最靈活、語法最簡潔 | 參數定義字符串易出錯、需單行 | 較啰嗦、需聲明游標、PREPARE 兩步 |