8

这是一个例子。

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

$mysqli->begin_transaction();

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

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

$mysqli->commit();

任何一个查询都没有任何错误,但是调用时commit()这些值没有存储在数据库中。如果拆分为单独的查询并通过query().

4

3 回答 3

4

你不应该使用多重查询。重写你的代码如下

$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("s", $name);
$name = 'pratik5';
$stmt->execute();
$name = 'pratik6';
$stmt->execute();

$mysqli->commit();
于 2016-10-17T13:46:09.080 回答
4
$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. 发送“请求设置选项”(如 Wireshark 中所示)帧,其中“多语句”标志设置为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 参考:“命令不同步”

于 2020-02-24T13:32:40.310 回答
0

首先,在您展示的示例中,您不应该使用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);
于 2020-02-27T22:16:26.313 回答