| 方法 | 適用場景 | 限制 |
|---|---|---|
| 方法 1 | 無宿主變量的非查詢語句 | 不可用於 SELECT |
| 方法 2 | 輸入宿主變量個數/類型固定的非查詢語句 | 不可用於 SELECT |
| 方法 3 | 選擇列表項與輸入宿主變量個數固定的動態 SQL | 通用,支援 SELECT 及所有 DML/DDL/DCL |
本文重點講解功能最全的方法 3。
INSERT ... VALUES(:a)、DELETE ... WHERE name=:aCREATE TABLE ...、GRANT ...COMMITSELECT name FROM tbl WHERE no=xxx不支援:對象名/列名作為宿主變量(如 INSERT INTO :a VALUES(:b))。
EXEC SQL PREPARE statement_name FROM { :host_string | string_literal };
statement_name:預編譯器標識符(非字符串變量)。host_string:包含 SQL 文本的宿主變量(如 char sql_buf[1024])。string_literal:直接寫死的 SQL 字符串。EXEC SQL DECLARE cursor_name CURSOR FOR statement_name;
cursor_name:游標名稱。statement_name:PREPARE 時使用的標識符。EXEC SQL OPEN cursor_name [USING host_variable_list];
USING 子句綁定輸入宿主變量(對應 SQL 中的 :var)。EXEC SQL FETCH cursor_name INTO host_variable_list;
sqlca.sqlcode == 1403 (NO_DATA_FOUND)。EXEC SQL CLOSE cursor_name;
/* 大組成員表結構 */
struct GrpMember {
char str_bgID[22]; /* 大組ID */
char str_matID[22]; /* 素材ID */
...
};
/* 素材檢索:根據輸入結構動態構建 WHERE 條件 */
void MatSearch(struct GrpMember *in) {
char buf[256];
EXEC SQL BEGIN DECLARE SECTION;
char sql_buf[1024];
struct GrpMember ret;
EXEC SQL END DECLARE SECTION;
/* 1. 構建基礎 SQL */
strcpy(sql_buf, "SELECT * FROM TBL_GRPMEMBER WHERE 1=1 ");
/* 2. 根據非空字段動態拼接條件 */
if (!isEmpty(in->str_bgID)) {
sprintf(buf, "AND tbl_str_bgID = %s ", in->str_bgID);
strcat(sql_buf, buf);
}
/* ... 其他字段類似 ... */
/* 3. PREPARE */
EXEC SQL PREPARE sql_stmt FROM :sql_buf;
/* 4. DECLARE CURSOR */
EXEC SQL DECLARE c1 CURSOR FOR sql_stmt;
/* 5. OPEN:執行查詢 */
EXEC SQL OPEN c1;
/* 6. FETCH 循環處理結果 */
while (1) {
EXEC SQL FETCH c1 INTO :ret; /* 實際應逐項 INTO,逗號分隔 */
if (sqlca.sqlcode == 1403) break; /* NO_DATA_FOUND */
/* 處理 ret ... */
}
/* 7. CLOSE */
EXEC SQL CLOSE c1;
}
EXEC SQL BEGIN/END DECLARE SECTION 區塊中聲明。FETCH INTO :ret 中 ret 為結構體變量,Pro*C 自動處理成員映射;若用指針需確保內存有效。sqlca.sqlcode,非零即異常。WHERE name = :bv)而非字符串拼接,否則極易遭受注入攻擊。Pro*C 動態 SQL 方法 3 通過 PREPARE/DECLARE/OPEN/FETCH/CLOSE 五步流程,實現了運行時構建並執行任意 SQL(含查詢)的能力。核心優勢在於靈活性:WHERE 條件、排序、甚至部分 DDL 可動態生成。生產環境中務必結合綁定變量防注入,並做好錯誤碼檢查與資源釋放。