multi_query не работает, когда я использую транзакции mysql с mysqli в php

Вот пример моего кода

$mysqli = new mysqli("localhost", "root", "123", "temp");

var_dump($mysqli);

$mysqli->begin_transaction();

$sql1 = "insert into test (Name) values ('pratik5');";

$sql1 .= "insert into test (Name) values ('pratik6');";

$test = $mysqli->multi_query($sql1);

$mysqli->commit();

1 ответ

$mysqli->multi_query($sql1);
$mysqli->commit(); // This will result in a "Commands out of sync; you can't run this command now" error.

Вышеупомянутое идентично:

$mysqli->multi_query($sql1);
$mysqli->query("commit"); // This will result in a "Commands out of sync; you can't run this command now" error.

Что бы вы ни вложили $mysqli->query("...");, это приведет к "Commands out of sync" ошибка, даже с простым SELECT 1;

Причина этой ошибки в том, что ->commit() операция выполняет один запрос (commit;). Однако результаты предыдущих запросов не были прочитаны.

Когда сингл query() Если используется операция, сервер MySQL ответит кадром ответа, который зависит от оператора запроса.

Когда используешь multi_query(), на уровне протокола связи MySQL происходит следующее:

  1. Фрейм "Request Set Option" (как показано в Wireshark) отправляется с установленным флагом "multi statement" в положение ON.
  2. Кадр, содержащий всю строку, переданную в multi_query() как запрос.
  3. Сервер MySQL отвечает ответом, который может содержать разные наборы результатов. То же самое и при вызове хранимой процедуры.

Решение 1

Если вы хотите использовать multi_query()ты должен иметь свой start transaction / commit операции в его составе:

$mysqli = new mysqli("localhost", "root", "123", "temp");

$sql1 = "start transaction;"; // $mysqli->begin_transaction() is a convenience function for simply doing this.
$sql1 .= "insert into test (Name) values ('pratik5');";
$sql1 .= "insert into test (Name) values ('pratik6');";
$sql1 .= "commit;"; // $mysqli->commit() is a convenience function for simply doing this.

$mysqli->multi_query($sql1);

/* As in "Solution 2", if you plan to perform other queries on DB resource
   $mysqli after this, you must consume all the resultsets:
// This loop ensures that all resultsets are processed and consumed:
do {
    $mysqli->use_result();
}
while ($mysqli->next_result());
*/

Решение 2

$mysqli = new mysqli("localhost", "root", "123", "temp");

$mysqli->begin_transaction();

$sql1 = "insert into test (Name) values ('pratik5');";
$sql1 .= "insert into test (Name) values ('pratik6');";

$mysqli->multi_query($sql1);

// This loop ensures that all resultsets are processed and consumed:
do {
    $mysqli->use_result();
}
while ($mysqli->next_result());

// Now that all resultsets are processed, a single query `commit;` can happen:
$mysqli->commit();

Справочник MySQL: "Команды не синхронизированы".

Вы не должны использовать несколько запросов. Перепишите ваш код следующим образом

$mysqli->begin_transaction();

$mysqli->query("insert into test (Name) values ('pratik5')");
$mysqli->query("insert into test (Name) values ('pratik6')");

$mysqli->commit();

или, для реальных вставок,

$mysqli->begin_transaction();

$stmt = $mysqli->prepare("insert into test (Name) values (?)");
$stmt->bind_param("2", $name);
$name = 'pratik5';
$stmt->execute();
$name = 'pratik6';
$stmt->execute();

$mysqli->commit();

Прежде всего, в показанном вами примере вам не следует использоватьmulti_query(). У вас есть два отдельных оператора, которые должны выполняться отдельно. См . Ответ вашего здравого смысла

multi_query()следует использовать редко. Его следует использовать только в ситуациях, когда у вас уже есть строка, состоящая из нескольких запросов, которой вы абсолютно доверяете. Никогда не разрешайте ввод переменных вmulti_query()!

Зачем commit() не работает после multi_query()?

По правде говоря, MySQL выдает ошибку commit(), но mysqli не может выдать ошибку как исключение. Я не знаю, ошибка это или техническое ограничение. Вы можете увидеть ошибку, если проверите вручную$mysqli->errorсвойство. Вы должны увидеть следующее сообщение об ошибке:

Команды не синхронизированы; вы не можете запустить эту команду сейчас

Однако исключение генерируется правильно после вызова use_result() или store_result().

$mysqli->multi_query(/* SQLs */);
$mysqli->commit();
$r = $mysqli->use_result(); // Uncaught mysqli_sql_exception: Commands out of sync; you can't run this command now

Причина, по которой MySQL сообщает, что команды не синхронизированы, заключается в том, что вы можете выполнять только одну команду за раз в каждом сеансе MySQL. Однако,multi_query()отправляет всю строку SQL в MySQL за одну команду, и пусть MySQL выполняет запросы асинхронно. Сначала PHP будет ждать завершения выполнения первого набора результатов, создающего не-DML-запрос (не INSERT, UPDATE или DELETE), прежде чем элемент управления будет передан обратно в сценарий PHP. Это позволяет MySQL запускать остальную часть SQL на сервере и буферизовать результаты, в то время как вы можете выполнять некоторые другие операции в PHP и собирать результаты позже. Вы не можете запустить другую команду SQL в том же соединении, пока не переберете все результаты предыдущих асинхронных запросов.

Как указано в другом ответе, commit()попытается выполнить другую команду в том же соединении. Вы получаете ошибку рассинхронизации, потому что вы просто не завершили обработкуmulti_query() команда.

Блокирующий цикл MySQL i

За каждым асинхронным запросом должен следовать блокирующий цикл в mysqli. Блокирующий цикл будет перебирать все выполненные запросы на сервере и получать результаты один за другим, которые затем можно обработать в PHP. Вот пример такого цикла:

do {
    $result = $mysqli->use_result();
    if ($result) {
        // process the results here
        $result->free();
    }
} while ($mysqli->next_result()); // Next result will block and wait for next query to finish
$mysqli->store_result(); // Needed to fetch the error as exception

У вас всегда должен быть цикл блокировки, даже если вы знаете, что запросы не будут давать никаких наборов результатов.

Решение

Держись подальше от multi_query()! В большинстве случаев есть лучший способ выполнения файлов SQL. Если у вас есть отдельные запросы, не объединяйте их вместе, а выполняйте каждый по отдельности.

Если вам действительно нужно использовать multi_query(), и вы хотите заключить его в транзакцию, вы должны поставить commit()после петли блокировки. Все результаты необходимо повторить, прежде чем вы сможете выполнитьCOMMIT; команда.

$mysqli->begin_transaction();

$sql1 = "insert into test (Name) values ('pratik5');";
$sql1 .= "insert into test (Name) values ('pratik6');";

$test = $mysqli->multi_query($sql1);

// $mysqli->commit();

do {
    $result = $mysqli->use_result();
    if ($result) {
        // process the results here
        $result->free();
    }
} while ($mysqli->next_result());
$mysqli->store_result(); // To fetch the error as exception

$mysqli->commit();

Конечно, чтобы увидеть любую из ошибок mysqli, вам необходимо включить режим исключений. Просто поставьте эту строку передnew mysqli():

mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
Другие вопросы по тегам