MySQLのエラー『Command Out of Sync』の解決方法

MySQLのエラー『Command Out of Sync』の解決方法

MySQLで「Commands out of sync; you can’t run this command now」というエラーが発生するのは、クライアントアプリケーションがMySQLのプロトコルの状態と同期していないコマンドを送信した場合に起きる。特にCやPHPのような言語でMySQLクライアントライブラリ(libmysqlclientなど)を使っているときによく見られる。接続中に実行されたコマンドが完了する前に次のコマンドを送信するとこのエラーになる。

1. エラーが発生する典型的なパターン

結果セットを最後まで読み込んでいない状態で次のクエリを発行するとエラーになる。

MYSQL_RES *res = mysql_store_result(conn);
mysql_query(conn, "SELECT * FROM another_table"); // ここでエラー発生

2. エラーの発生条件

  • mysql_store_result() または mysql_use_result() の後に結果をすべて処理していない
  • 前のクエリの結果セットを解放(mysql_free_result)していない
  • クエリが非同期的に処理された後、正しくハンドリングしていない

3. 対処方法:結果セットをすべて処理する

クエリ結果をループで全て処理することで次のクエリ実行が可能になる。

MYSQL_RES *res = mysql_store_result(conn);
MYSQL_ROW row;
while ((row = mysql_fetch_row(res)) != NULL) {
// 行の処理
}
mysql_free_result(res);
mysql_query(conn, "SELECT * FROM another_table"); // 正常に実行される

4. mysql_use_result() 使用時の注意点

mysql_use_result() は結果を一度に全て取得せず、逐次フェッチする方式。全ての行を取得せずに次のクエリを実行するとエラーが起きる。

// 全件取得しないと次のクエリが失敗
MYSQL_RES *res = mysql_use_result(conn);
while ((row = mysql_fetch_row(res)) != NULL) {
// 全件読む必要あり
}
mysql_free_result(res);

5. 結果セットがNULLでもfree_resultする

クエリが結果を返さない場合でも、mysql_store_result()がNULLを返すことがあり、必要に応じてfree_result()を呼ぶ必要がある。

MYSQL_RES *res = mysql_store_result(conn);
if (res != NULL) {
    mysql_free_result(res);
}

6. 複数のクエリを連続実行する場合の注意点

結果セットを毎回処理または解放してから次のクエリへ移行する必要がある。

mysql_query(conn, "SELECT * FROM users");
MYSQL_RES *res1 = mysql_store_result(conn);
mysql_free_result(res1);

mysql_query(conn, "SELECT * FROM orders");
MYSQL_RES *res2 = mysql_store_result(conn);
mysql_free_result(res2);

7. PHPで発生するケース

mysqliで一部の関数が結果の未処理でエラーになる。

$result = $mysqli->query("SELECT * FROM users");
// while($row = $result->fetch_assoc()) { } を忘れると次のクエリでエラー

8. PHPのmysqliで複数クエリを扱う場合

multi_queryを使用する場合、次のクエリを送る前に結果の処理が必要になる。

$mysqli->multi_query("SELECT * FROM users; SELECT * FROM orders;");
do {
  if ($result = $mysqli->store_result()) {
    while ($row = $result->fetch_row()) {
      // 結果処理
    }
    $result->free();
  }
} while ($mysqli->more_results() && $mysqli->next_result());

9. プリペアドステートメント使用時の注意点

プリペアドステートメントを使う場合でも、bind_resultとfetchを正しく処理しないと同様のエラーが出ることがある。

$stmt->bind_result($col1, $col2);
while ($stmt->fetch()) {
  // 値処理
}
// 次のexecuteの前にfetch完了が必要

10. ストアドプロシージャを呼び出す場合

プロシージャの中で複数のSELECTがある場合、すべての結果セットを処理しないと次のコマンドでエラーになる。

CALL my_proc(); // これが複数のSELECTを返す場合、全部読む必要あり

11. エラーの再現サンプルコード(C)

#include <mysql/mysql.h>

int main() {
    MYSQL *conn = mysql_init(NULL);
    mysql_real_connect(conn, "localhost", "user", "pass", "db", 0, NULL, 0);

    mysql_query(conn, "SELECT * FROM users");
    // 結果を読まずに次のクエリを実行
    mysql_query(conn, "SELECT * FROM orders"); // <-- ここで "Commands out of sync" エラー

    mysql_close(conn);
    return 0;
}

12. 対策まとめ

  • 全ての結果セットを取得してから次のクエリを実行する
  • mysql_free_result() を忘れない
  • multi_query や stored procedure では複数の結果を順に処理する
  • ライブラリ(PHP, C, Pythonなど)の仕様を理解する