首先,在您展示的示例中,您不应该使用multi_query()。您有两个单独的语句,应该分别执行。见Your Common Sense's answer
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() 命令的处理。
MySQLi 阻塞循环
每个异步查询都应该跟在 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);