MySQL C API 支援單次調用執行多條 SQL 語句(以分號分隔),需顯式啟用 CLIENT_MULTI_STATEMENTS。本文詳細說明啟用方式、結果集讀取循環、常見錯誤與最佳實踐。
// 方式 1:連接時指定
MYSQL *mysql = mysql_init(NULL);
mysql_real_connect(mysql, host, user, pass, db, port, NULL, CLIENT_MULTI_STATEMENTS);
// 方式 2:運行時開啟
mysql_set_server_option(mysql, MYSQL_OPTION_MULTI_STATEMENTS_ON);
const char *multi_sql =
"INSERT INTO t1 VALUES (1);"
"UPDATE t2 SET c=10 WHERE id=2;"
"SELECT * FROM t3 LIMIT 5;";
if (mysql_query(mysql, multi_sql) != 0) {
fprintf(stderr, "Error: %s\n", mysql_error(mysql));
}
do {
// 1. 獲取結果集(SELECT/SHOW/EXPLAIN 等返回結果集;INSERT/UPDATE 返回 NULL)
MYSQL_RES *result = mysql_store_result(mysql);
// 2. 處理結果集
if (result) {
// 有結果集:逐行讀取
MYSQL_ROW row;
while ((row = mysql_fetch_row(result))) {
// 處理 row...
}
mysql_free_result(result);
} else {
// 無結果集:檢查是否為錯誤
if (mysql_field_count(mysql) == 0) {
// 正常的 INSERT/UPDATE/DELETE
printf("Affected rows: %lld\n", mysql_affected_rows(mysql));
} else {
// 錯誤:如語法錯誤導致無結果集
fprintf(stderr, "Error: %s\n", mysql_error(mysql));
break;
}
}
// 3. 移動到下一個結果集
int status = mysql_next_result(mysql);
if (status > 0) {
fprintf(stderr, "Error: %s\n", mysql_error(mysql));
break;
}
} while (status == 0);
do {
MYSQL_RES *result = mysql_store_result(mysql);
mysql_free_result(result); // 釋放空結果集
} while (mysql_next_result(mysql) == 0);
| 錯誤 | 原因 | 解決 |
|---|---|---|
| Commands out of sync; you can't run this command now | 上一條多語句的結果集未讀完就發起新查詢 | 務必跑完 do-while 循環讀取所有結果集 |
| Lost connection to MySQL server during query | 單次多語句過大/過多超過 max_allowed_packet 或超時 | 拆分批次、調大 max_allowed_packet、net_read_timeout |
| SQL 注入風險 | 拼接用戶輸入構造多語句 | 嚴禁拼接,改用參數化或分批執行單條語句 |
PreparedStatement.addBatch() (Java) / executemany() (Python)。CLIENT_MULTI_STATEMENTS 在池創建時設置,避免借用連接時動態切換導致狀態不一致。