#Что означает неисправность
Ошибка клиента MySQL «Commands out of sync; you can’t run this command now» означает, что приложение пытается отправить новую команду, пока предыдущий результат или набор результатов ещё не обработан. Особенно часто это происходит в PHP mysqli после multi_query или CALL хранимой процедуры. Проблема относится к последовательности вызовов клиента, а не к повреждению таблицы; перезапуск mysqld не нужен.
#Возможные причины и признаки
| Возможная причина | Как узнать | Что делать |
|---|---|---|
| После multi_query остался второй результатДрайвер ожидает извлечения и освобождения всех результатов предыдущего набора команд. | Первый запрос возвращает строки, а следующий query внезапно даёт Commands out of sync. | Пройти more_results/next_result и освободить каждый mysqli_result. |
| CALL процедуры возвращает дополнительные наборыПроцедура или драйвер могут оставлять ещё результаты после первого набора строк. | Ошибка возникает только после CALL, обычный SELECT работает. | Использовать поддерживаемый драйвером порядок чтения всех результатов. |
| Результат из неблокирующего чтения ещё не освобождёнПри unbuffered fetch приложение прерывает выборку и отправляет новый запрос тем же соединением. | После вызова use_result/неполного fetch возникает ошибка порядка команд. | Прочитать оставшийся набор или вызвать free на соответствующем объекте результата. |
#Пошаговая диагностика и решение
- Определите, где возникает ошибка и какой драйвер используетсяВ журнале PHP найдите ошибку
Commands out of syncи строку исходного кода, где вызывается query/prepare/execute. Запишите, выполнялся ли до этого multi_query, CALL или use_result. Если используете PDO, Python либо нативный клиент, ищите документацию именно этого драйвера — примеры mysqli далее нельзя подставлять без изменений. - Проследите операции в одном соединенииВ редакторе кода найдите создание
$mysqli, затем все запросы до проблемного места. Составьте последовательность: multi_query → store_result → free → more_results → next_result → query. Если новый query стоит раньше окончания обработки всех результатов, ошибка ожидаема. Не пытайтесь устранить её открытием бесконечного числа новых соединений, поскольку это вызовет другие сбои. - Проверьте, не используется ли multi_query без необходимостиДля одного SELECT, INSERT или UPDATE в mysqli используйте обычный
queryили подготовленный запрос и освобождайте полученный mysqli_result. Multi_query необходим только тогда, когда явно требуется несколько SQL-операторов в одном вызове. Удаление лишнего multi_query снижает вероятность ошибки и не ослабляет защиту от SQL-инъекций. - Обрабатывайте каждый возвращённый набор по порядкуПосле успешного multi_query вызовите store_result для текущего результата; если это mysqli_result, прочитайте строки и освободите объект методом free. Затем проверьте more_results и вызовите next_result, чтобы перейти дальше, включая служебные результаты. Ошибку перехода проверяйте через errno/error, а не считайте любой false подтверждением завершения.
- Разделите работу с данными и SQL из пользовательского вводаНикогда не вставляйте в multi_query текст из HTML-формы, параметра URL или запроса внешней системы. Для значений используйте mysqli prepared statements с bind_param. При разборе нескольких запросов убедитесь, что SQL написан приложением, а не собран из пользовательских строк, иначе на исправленной цепочке можно открыть SQL-инъекцию.
- Проверьте обработку CALL хранимой процедурыЕсли сначала вызывается CALL, прочитайте все строки результата, а затем завершите извлечение остальных наборов в порядке, предусмотренном mysqli. Проверьте, что хранимая процедура не возвращает дополнительные SELECT, которые забыты в клиентском коде. В тестовом проекте временно замените сложную процедуру простой с двумя SELECT и убедитесь, что дальнейший SELECT теперь разрешён.
- Прогоните короткий безопасный пример в тестовом PHPСоздайте файл вне публичного каталога веб-сервера, настройте доступ к отдельной тестовой БД и поместите пример чтения результатов. Запускайте через
php имя_файла.phpв терминале, где установлен PHP CLI и расширение mysqli. Пароль берите из безопасной конфигурации или окружения, не пишите его в коде и не выводите в журнал. - Проверьте запрос после освобождения всех результатовКогда цикл по всем наборам завершится, выполните через то же соединение простой
SELECT 1 AS ok. Ожидается ok=1 и отсутствие Commands out of sync. Если ошибка повторилась, снимите трассу порядка вызовов, отдельно проверьте использование use_result и обработку исключений; верните прежнюю версию кода, если исправление нарушило чтение результатов.
#Команды для диагностики и проверки
SELECT 1 AS ok;#Пример для отдельной тестовой базы или учебного проекта
<?php
$mysqli = new mysqli(
getenv('DB_HOST') ?: '127.0.0.1',
getenv('DB_USER') ?: '',
getenv('DB_PASSWORD') ?: '',
getenv('DB_NAME') ?: ''
);
$mysqli->set_charset('utf8mb4');
$mysqli->multi_query('SELECT 1 AS first_value; SELECT 2 AS second_value');
do {
$result = $mysqli->store_result();
if ($result instanceof mysqli_result) {
while ($row = $result->fetch_assoc()) {
var_export($row); echo PHP_EOL;
}
$result->free();
}
if (!$mysqli->more_results()) {
break;
}
if (!$mysqli->next_result()) {
throw new RuntimeException($mysqli->error);
}
} while (true);
$result = $mysqli->query('SELECT 1 AS ok');
var_export($result->fetch_assoc());
$result->free();#Проверка результата и дальнейшие действия
Исправление подтверждено, если на одном и том же mysqli-соединении все два SELECT возвращают результаты, а следующий SELECT 1 выполняется без Commands out of sync. Если появляются неожиданные пустые результаты, проверьте очередность вызовов и ошибки next_result, а не пропускайте их. Откат — возвращение исходного кода из контроля версий, если новая обработка нарушает бизнес-логику.