php - 如何使用 mysqli_multi_query 识别导致错误的查询?

标签 php mysqli error-handling mysqli-multi-query multiple-select-query

使用 SO 上其他地方的示例来更好地捕获“隐藏”错误。虽然下面的代码将捕获并返回错误,但是否可以改进它以报告发生错误的查询?

使用下面的代码,输出是:

Columns: 18
Error: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'FRO inventory' at line 1

正在测试的代码:

$query = "SELECT * FROM orders WHERE location = 'IN' ORDER BY orderNum DESC LIMIT 20;";
$query .= "SELECT * FRO inventory";             //  With error
$ord = array();
$invent = array();

if(mysqli_multi_query($link, $query)) {
    do {
        // fetch results
        if($result = mysqli_store_result($link)) {
           echo 'Columns: ' . mysqli_field_count($link) . "<br>"; 
           while($row = mysqli_fetch_assoc($result)) {
                if(count($row) > 17)
                    $orders[] = $row;
                elseif(count($row) == 6)
                    $inv[] = $row;
            }
        }
        if(!mysqli_more_results($link))
            break;
        if(!mysqli_next_result($link)) {
            // report error
            echo 'Error: ' . mysqli_error($link);
            break;
        }
    } while(true);
    mysqli_free_result($result);
}

最佳答案

这种方法不仅可以提高错误消息的质量,还可以改进您处理结果集的方式。

$q["Orders"] = "SELECT * FROM orders WHERE location = 'IN' ORDER BY orderNum DESC LIMIT 20";
$q["Inventory"] = "SELECT * FRO inventory";

if (!$link = mysqli_connect("host", "user", "pass", "db")) {
    echo "Failed to connect to MySQL: " , mysqli_connect_error();
} elseif (mysqli_multi_query($link, implode(';', $q))) {
    do {
        $q_key = key($q);                                 // current query's key name (Orders or Inventory)
        if ($result = mysqli_store_result($link)) {       // if a result set... SELECTs do
            while ($row = mysqli_fetch_assoc($result)) {  // if one or more rows, iterate all
                $rows[$q_key][] = $row;
            }
            mysqli_free_result($result);
            echo "<div><pre>" . var_export($rows[$q_key], true) . "</pre></div>";
        }
    } while (next($q) && mysqli_more_results($link) && mysqli_next_result($link));
}
if ($mysqli_error = mysqli_error($link)) {                // check & declare variable in same step to avoid duplicate func call
    echo "<div style=\"color:red;\">Query Key = " , key($q) , ", Query = " , current($q) , ", Syntax Error = $mysqli_error</div>";
}

第一次查询时出错: 如果您的第一个查询尝试访问指定数据库中不存在的表,例如:ordersXYZ 数组 $rows不会存在,没有 var_export()将发生,您将看到此响应:

Query Key = Orders, Query = SELECT * FROM ordersXYZ WHERE location='IN' ORDER BY orderNum DESC LIMIT 20, Syntax Error = Table '[someDB].ordersXYZ' doesn't exist

第二次查询出错: 如果您的第一个查询成功,但您的第二个查询尝试访问一个不存在的表,例如:inventory2
$rows["Orders"]将保存所需的行数据并且将是 var_export() '编辑,$row["Inventory"]将不存在,您将看到此响应:

Query Key = Inventory, Query = SELECT * FROM inventory2, Syntax Error = Table '[someDB].inventory2' doesn't exist

没有错误: 如果两个查询都没有错误,您的 $rows数组将填充所需的数据和 var_export() 'ed,不会有错误响应。将查询到的数据保存在$rows中, 您可以从 $rows["Orders"] 访问您想要的内容和 $rows["Inventory"] .


注意事项:

  1. 您可能会注意到我同时进行了变量声明和条件检查,这使代码更加简洁,但一些开发人员更愿意避免这种情况。

  2. 因为我的方法使用 implode()elseif 上有一个分号行,请确保不要在查询中添加尾随分号。

  3. 这组查询总是返回一个结果集,因为所有都是 SELECT 查询,如果您有一个混合的查询集合 affect_rows ,您可能会在此链接 ( https://stackoverflow.com/a/22469722/2943403 ) 中找到一些有用的信息。

  4. mysqli_multi_query()一旦出现错误,将立即停止运行查询。如果您希望捕获“所有”错误,您会发现永远不会超过一个。

  5. 像 OP 的问题和解决方案那样编写条件断点是不可取的。虽然在其他情况下可以正确使用自定义断点,但对于这种情况,断点应位于 while() 内部。 do()的声明 block 。

  6. 返回零行的查询不会导致错误消息——它只是不会在 $rows 中创建任何子数组。因为while()不会进入循环。

  7. 通过使用 key()函数,OP 的 if/elseif可以避免对每个结果集行中的列进行计数的条件。这是更好的做法,因为在某些情况下,每次迭代都运行一个条件可能会变得昂贵。请注意,数组指针在 $q 内部被提前在每个 do() 的末尾迭代。这是您在 php 手册页上找不到的附加技术;它允许 key()按预期工作。

  8. 当然还有 <div><pre>var_export()...</pre></div>行可以从您的工作代码中删除——这纯粹是为了演示。

  9. 如果您要在此代码块之后运行任何更多的查询以重用变量,请务必清除所有使用的变量,以免残留数据干扰。例如$mysqli_error=null; // clear errors & reset($q); // reset array pointer .

  10. 自行决定注意这个有些模糊的警告:http://php.net/manual/en/mysqli.use-result.php :

One should not use mysqli_use_result() if a lot of processing on the client side is performed, since this will tie up the server and prevent other threads from updating any tables from which the data is being fetched.

  1. 最后,也是最重要的,出于安全原因,不要公开显示查询或查询错误信息——您不希望不怀好意的人看到这种反馈。同样重要的是,始终保护您的查询免受注入(inject)攻击。如果您的查询包括用户提供的数据,您需要在使用数据之前过滤/清理数据至死 mysqli_multi_query() .事实上,在处理用户输入时,我非常强烈的建议是远离 mysqli_multi_query()并使用 mysqlipdo为您的数据库交互准备好的语句以获得更高级别的安全性。

关于php - 如何使用 mysqli_multi_query 识别导致错误的查询?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12232974/

相关文章:

javascript - jQuery Ajax 脚本相互淘汰

php - 从 MySQL/PHP 中的多个表中删除的正确语法

php - 为什么404或其他自定义IIS错误页面中的POST数组为空?

php - magento 无效的交易电子邮件代码

PHP/MySQL 双循环

php - UPDATE 语句不起作用,但适用于 phpMyAdmin 和实时主机

PHP Mysql 字段值基于另一个字段值

python - 如何抑制警告 QPixmap : It is not safe to use pixmaps outside the GUI thread

json - REST API 中的验证错误响应

php - 使用现有标记渲染 Zend_Form 的最佳方法