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,因此 mysql_real_connect()flags 引數 CLIENT_MULTI_STATEMENTS 等同於 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);