3.6.3 多語句執行支援

預設情況下,mysql_real_query()mysql_query() 將其語句字串參數解譯為要執行的單一語句,並且您會根據語句是否產生結果集(一組列,如 SELECT)或受影響的列計數(如 INSERTUPDATE 等等)來處理結果。

MySQL 也支援執行包含多個以分號 (;) 字元分隔的語句的字串。此功能透過在您使用 mysql_real_connect() 連接到伺服器時,或在連線後呼叫 mysql_set_server_option() 時指定的特殊選項來啟用。

執行多語句字串可能會產生多個結果集或列計數指示器。處理這些結果的方法與單一語句的情況不同:在處理第一個語句的結果後,必須檢查是否有更多結果存在,如果有,則依序處理它們。為了支援多結果處理,C API 包含 mysql_more_results()mysql_next_result() 函數。這些函數在迴圈的結尾使用,只要有更多結果可用,迴圈就會持續迭代。 未能以這種方式處理結果可能會導致與伺服器的連線中斷。

如果您執行 CALL 儲存程序語句,也需要進行多結果處理。儲存程序的結果具有以下特徵

  • 程序中的語句可能會產生結果集(例如,如果它執行 SELECT 語句)。這些結果集會按照它們在程序執行時產生的順序返回。

    一般而言,呼叫者無法知道程序會返回多少個結果集。程序執行可能取決於迴圈或條件語句,這些語句會導致執行路徑在每次呼叫時有所不同。因此,您必須準備好檢索多個結果。

  • 程序的最終結果是一個狀態結果,其中不包含結果集。該狀態指示程序是否成功或發生錯誤。

多語句和結果功能只能與 mysql_real_query()mysql_query() 一起使用。它們不能與預先準備語句介面一起使用。預先準備語句處理程式定義為僅適用於包含單一語句的字串。請參閱 第 6 章,C API 預先準備語句介面

要啟用多語句執行和結果處理,可以使用以下選項

  • mysql_real_connect() 函數有一個 flags 引數,其中兩個選項值是相關的

    • CLIENT_MULTI_RESULTS 使用戶端程式能夠處理多個結果。如果您執行產生結果集的儲存程序的 CALL 語句,必須 啟用此選項。否則,此類程序會導致錯誤 Error 1312 (0A000): PROCEDURE proc_name can't return a result set in the given contextCLIENT_MULTI_RESULTS 預設為啟用。

    • CLIENT_MULTI_STATEMENTS 使 mysql_real_query()mysql_query() 能夠執行包含多個以分號分隔的語句的語句字串。此選項也會隱式啟用 CLIENT_MULTI_RESULTS,因此傳遞 CLIENT_MULTI_STATEMENTSflags 引數給 mysql_real_connect(),等同於傳遞 CLIENT_MULTI_STATEMENTS | CLIENT_MULTI_RESULTS 的引數。也就是說,CLIENT_MULTI_STATEMENTS 足以啟用多語句執行和所有多結果處理。

  • 連線到伺服器後,您可以使用 mysql_set_server_option() 函數,並傳遞 MYSQL_OPTION_MULTI_STATEMENTS_ONMYSQL_OPTION_MULTI_STATEMENTS_OFF 的引數來啟用或停用多語句執行。使用此函數啟用多語句執行也會啟用多語句字串的 簡單 結果處理,其中每個語句產生單一結果,但不足以允許處理產生結果集的儲存程序。

以下程序概述了處理多語句的建議策略

  1. CLIENT_MULTI_STATEMENTS 傳遞給 mysql_real_connect(),以完全啟用多語句執行和多結果處理。

  2. 在呼叫 mysql_real_query()mysql_query() 並驗證其成功後,進入一個迴圈,您可以在其中處理語句結果。

  3. 在迴圈的每次迭代中,處理目前的語句結果,檢索結果集或受影響的列計數。如果發生錯誤,則退出迴圈。

  4. 在迴圈的結尾,呼叫 mysql_next_result() 以檢查是否存在其他結果,如果存在,則啟動對其的檢索。如果沒有更多結果可用,則退出迴圈。

以下顯示了上述策略的一種可能的實作方式。迴圈的最後一部分可以簡化為對 mysql_next_result() 是否返回非零值的簡單測試。編寫的程式碼區分了沒有更多結果和錯誤,這使得可以為後者發印訊息。

/* connect to server with the CLIENT_MULTI_STATEMENTS option */
if (mysql_real_connect (mysql, host_name, user_name, password,
    db_name, port_num, socket_name, CLIENT_MULTI_STATEMENTS) == NULL)
{
  printf("mysql_real_connect() failed\n");
  mysql_close(mysql);
  exit(1);
}

/* execute multiple statements */
status = mysql_query(mysql,
                     "DROP TABLE IF EXISTS test_table;\
                      CREATE TABLE test_table(id INT);\
                      INSERT INTO test_table VALUES(10);\
                      UPDATE test_table SET id=20 WHERE id=10;\
                      SELECT * FROM test_table;\
                      DROP TABLE test_table");
if (status)
{
  printf("Could not execute statement(s)");
  mysql_close(mysql);
  exit(0);
}

/* process each statement result */
do {
  /* did current statement return data? */
  result = mysql_store_result(mysql);
  if (result)
  {
    /* yes; process rows and free the result set */
    process_result_set(mysql, result);
    mysql_free_result(result);
  }
  else          /* no result set or error */
  {
    if (mysql_field_count(mysql) == 0)
    {
      printf("%lld rows affected\n",
            mysql_affected_rows(mysql));
    }
    else  /* some error occurred */
    {
      printf("Could not retrieve result set\n");
      break;
    }
  }
  /* more results? -1 = no, >0 = error, 0 = yes (keep looping) */
  if ((status = mysql_next_result(mysql)) > 0)
    printf("Could not execute statement\n");
} while (status == 0);

mysql_close(mysql);