首先,在您展示的示例中,您不应该使用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()
命令的处理。
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);