PHP mysqli 子查询返回 Impossible WHERE 在读取 const 表后注意到

Posted

技术标签:

【中文标题】PHP mysqli 子查询返回 Impossible WHERE 在读取 const 表后注意到【英文标题】:PHP mysqli subquery return Impossible WHERE noticed after reading const tables 【发布时间】:2018-12-12 00:19:37 【问题描述】:

所以,我知道这是一个非常普遍的错误,但我没有更好的线索来了解我的查询发生了什么,也不能比这更好地解释它,这要感谢 mysql 没有给 **** UX,请耐心等待。

我正在使用 php 7.2.1 和 MySQL 5.7。

我有这个问题:

SELECT
    sp.*
FROM
    `status` s
    LEFT JOIN status_pedido sp ON sp.status_pedido_id = s.status_pedido_id 
WHERE
    s.status_id = (
SELECT
    `status`.status_id 
FROM
    pedido_item_status p1
    LEFT JOIN `status` ON `status`.status_id = p1.status_id
    LEFT JOIN pedido_item ON pedido_item.pedido_item_id = p1.pedido_item_id
    INNER JOIN ( SELECT MAX( pi.cadastrado ) AS maxcadastro FROM pedido_item_status pi GROUP BY pi.pedido_item_id ) p2 ON ( p1.cadastrado = p2.maxcadastro ) 
    AND p1.excluido IS NULL 
WHERE
    p1.pedido_id = 15720 
ORDER BY
    `status`.sta_ordem ASC 
    LIMIT 1 
    )

它在 Navicat 或 Heidi 上运行良好,并准确返回 1 个结果。但是当我在 PHP mysqli->query 上执行时返回空行:

object(mysqli_result)#135 (5)  ["current_field"]=> int(0) ["field_count"]=> int(11) ["lengths"]=> NULL ["num_rows"]=> int(0) ["type"]=> int(0) 

在 Navicat 上的解释返回以下内容:

+----+-------------+-------------+--------+-------------------+-------------+---------+---------------------------+-------+---------------------------------------------------------------------+--+
| id | select_type |    table    |  type  |   possible_keys   |     key     | key_len |            ref            | rows  |                                Extra                                |  |
+----+-------------+-------------+--------+-------------------+-------------+---------+---------------------------+-------+---------------------------------------------------------------------+--+
|  1 | PRIMARY     | s           | const  | PRIMARY           | PRIMARY     |       4 | const                     |     1 |                                                                     |  |
|  1 | PRIMARY     | sp          | const  | PRIMARY           | PRIMARY     |       4 | const                     |     1 |                                                                     |  |
|  2 | SUBQUERY    | p1          | ref    | idx_1,idx_2,idx_3 | idx_1       |      10 | const,const               |     5 | Using index condition; Using where; Using temporary; Using filesort |  |
|  2 | SUBQUERY    | status      | eq_ref | PRIMARY           | PRIMARY     |       4 | emgraf2.p1.status_id      |     1 |                                                                     |  |
|  2 | SUBQUERY    | pedido_item | eq_ref | PRIMARY           | PRIMARY     |       4 | emgraf2.p1.pedido_item_id |     1 | Using index                                                         |  |
|  2 | SUBQUERY    | <derived3>  | ref    | <auto_key0>       | <auto_key0> |       5 | emgraf2.p1.cadastrado     |    10 | Using index                                                         |  |
|  3 | DERIVED     | pi          | ALL    | idx_2,idx_3       |             |         |                           | 18750 | Using temporary; Using filesort                                     |  |
+----+-------------+-------------+--------+-------------------+-------------+---------+---------------------------+-------+---------------------------------------------------------------------+--+

在 PHP 上返回:

array(10) 
["id"]=>
string(1) "1"
["select_type"]=>
string(7) "PRIMARY"
["table"]=>
NULL
["type"]=>
NULL
["possible_keys"]=>
NULL
["key"]=>
NULL
["key_len"]=>
NULL
["ref"]=>
NULL
["rows"]=>
NULL
["Extra"]=>
string(51) "Impossible WHERE noticed after reading const tables"

array(10) 
["id"]=>
string(1) "2"
["select_type"]=>
string(8) "SUBQUERY"
["table"]=>
string(2) "p1"
["type"]=>
string(3) "ref"
["possible_keys"]=>
string(17) "idx_1,idx_2,idx_3"
["key"]=>
string(5) "idx_1"
["key_len"]=>
string(2) "10"
["ref"]=>
string(11) "const,const"
["rows"]=>
string(1) "5"
["Extra"]=>
string(67) "Using index condition; Using where; Using temporary; Using filesort"

array(10) 
["id"]=>
string(1) "2"
["select_type"]=>
string(8) "SUBQUERY"
["table"]=>
string(6) "status"
["type"]=>
string(6) "eq_ref"
["possible_keys"]=>
string(7) "PRIMARY"
["key"]=>
string(7) "PRIMARY"
["key_len"]=>
string(1) "4"
["ref"]=>
string(20) "emgraf2.p1.status_id"
["rows"]=>
string(1) "1"
["Extra"]=>
NULL

array(10) 
["id"]=>
string(1) "2"
["select_type"]=>
string(8) "SUBQUERY"
["table"]=>
string(11) "pedido_item"
["type"]=>
string(6) "eq_ref"
["possible_keys"]=>
string(7) "PRIMARY"
["key"]=>
string(7) "PRIMARY"
["key_len"]=>
string(1) "4"
["ref"]=>
string(25) "emgraf2.p1.pedido_item_id"
["rows"]=>
string(1) "1"
["Extra"]=>
string(11) "Using index"

array(10) 
["id"]=>
string(1) "2"
["select_type"]=>
string(8) "SUBQUERY"
["table"]=>
string(10) "<derived3>"
["type"]=>
string(3) "ref"
["possible_keys"]=>
string(11) "<auto_key0>"
["key"]=>
string(11) "<auto_key0>"
["key_len"]=>
string(1) "5"
["ref"]=>
string(21) "emgraf2.p1.cadastrado"
["rows"]=>
string(2) "10"
["Extra"]=>
NULL

array(10) 
["id"]=>
string(1) "3"
["select_type"]=>
string(7) "DERIVED"
["table"]=>
string(2) "pi"
["type"]=>
string(3) "ALL"
["possible_keys"]=>
string(11) "idx_2,idx_3"
["key"]=>
NULL
["key_len"]=>
NULL
["ref"]=>
NULL
["rows"]=>
string(5) "18750"
["Extra"]=>
string(31) "Using temporary; Using filesort"

据我从类似问题中了解到,这与客户端保持连接并在分析整个查询后执行查询有关,而 PHP 只是在内部 WHERE 似乎返回 0 行时停止。但我不知道如何在我的查询中解决这个问题,因为我没有在 SQL 上设置任何变量,只是创建子查询。

还有另一个问题:完全相同的查询在另一个代码块上运行,我不明白为什么会这样。这是有问题的代码:

$sql_sta = "SELECT pis.pedido_item_status_id, pi.pedido_item_id, pi.status_id, p.pedido_id, psp.pedido_status_pedido_id, sp.stp_ordem
            FROM pedido_item_status pis                
            LEFT JOIN pedido_item pi ON pis.pedido_item_id = pi.pedido_item_id
            LEFT JOIN pedido p ON pi.pedido_id = p.pedido_id
            LEFT JOIN status_pedido sp ON sp.status_pedido_id = p.status_pedido_id 
            LEFT JOIN pedido_status_pedido psp ON (psp.status_pedido_id = sp.status_pedido_id AND psp.pedido_id = p.pedido_id)
            WHERE pis.pedido_item_status_id = $_GET['del']
            LIMIT 1";

$res_sta = ClassDb::query($sql_sta);
$status_del = $res_sta->fetch_assoc();

$sql_ped = "SELECT sp.* FROM `status` s
            LEFT JOIN status_pedido sp ON sp.status_pedido_id = s.status_pedido_id
            WHERE s.status_id = (
                SELECT `status`.status_id FROM pedido_item_status p1
                LEFT JOIN `status` ON `status`.status_id = p1.status_id
                LEFT JOIN pedido_item ON pedido_item.pedido_item_id = p1.pedido_item_id
                INNER JOIN ( SELECT pi.pedido_id, MAX( pi.cadastrado ) AS maxcadastro FROM pedido_item_status pi GROUP BY pi.pedido_item_id ) p2
                ON ( p1.cadastrado = p2.maxcadastro ) AND p1.excluido IS NULL 
                WHERE p1.pedido_id = 15720
                ORDER BY `status`.sta_ordem ASC 
                LIMIT 1
            )";
$res_ped = ClassDb::query($sql_ped);

这是工作代码:

$sql_sta = "SELECT sp.* FROM pedido_item pi
            LEFT JOIN pedido p ON pi.pedido_id = p.pedido_id
            LEFT JOIN status_pedido sp ON sp.status_pedido_id = p.status_pedido_id 
            WHERE pi.pedido_item_id = $_POST['pedido_item_id']";
$res_sta = ClassDb::query($sql_sta);
$status_antigo = $res_sta->fetch_assoc();

$sql_ped = "SELECT sp.* FROM `status` s
            LEFT JOIN status_pedido sp ON sp.status_pedido_id = s.status_pedido_id
            WHERE s.status_id = (
                SELECT `status`.status_id FROM pedido_item_status p1
                LEFT JOIN `status` ON `status`.status_id = p1.status_id
                LEFT JOIN pedido_item ON pedido_item.pedido_item_id = p1.pedido_item_id
                INNER JOIN ( SELECT MAX( pi.cadastrado ) AS maxcadastro FROM pedido_item_status pi GROUP BY pi.pedido_item_id ) p2
                ON ( p1.cadastrado = p2.maxcadastro ) AND p1.excluido IS NULL 
                WHERE p1.pedido_id = $_POST['pedido_id']
                ORDER BY `status`.sta_ordem ASC 
                LIMIT 1
            )";
$res_ped = ClassDb::query($sql_ped);
$status_atual = $res_ped->fetch_assoc();

它们之间的唯一区别是我首先运行不同的查询来检索pedido_id,我尝试将变量更改为静态值并注释第一个查询,输出仍然相同,而且,工作代码在表单简单 POST 之后运行,在我插入一行之后,而从 ajax GET 请求运行有问题,在我更新一行以软删除它之后(将列 excluido 设置为 NOW(),将其视为 @ 987654332@ 来自 Laravel)。尝试在 Navicat 上手动运行查询,效果很好。

由于帖子已经太长,表又大又复杂,假设结果在那里,所有列都存在,查询在Navicat上执行良好并返回结果,并且在工作时也返回结果具有相同查询的代码块。

Edit1:没有 PHP 错误或异常,也没有 MYSQLI 错误,我已经将它们设置为显示。查询只返回空结果,代码正常运行。

编辑 2:尝试直接在 MySQL Shell 上运行,结果与 Navicat 或 Heidi 相同:

+------------------+-----------+----------------+-----------+----------+------------+------------+---------+----------+-----------+-----------+
| status_pedido_id | stp_nome  | stp_observacao | stp_ordem | excluido | cadastrado | atualizado | stp_cor | stp_icon | stp_alert | status_id |
+------------------+-----------+----------------+-----------+----------+------------+------------+---------+----------+-----------+-----------+
|                7 | Cancelado | NULL           |         8 | NULL     | NULL       | NULL       | NULL    | NULL     | NULL      |         9 |
+------------------+-----------+----------------+-----------+----------+------------+------------+---------+----------+-----------+-----------+
1 row in set (0.0269 sec)

【问题讨论】:

ini_set('display_errors', 1); ini_set('log_errors',1); error_reporting(E_ALL); mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT); 添加到脚本的顶部。这将强制任何 mysqli_ 错误生成您可以在浏览器上看到的异常以及正常的 PHP 错误。 已经设置好了,没有PHP或mysql错误。 警告:使用mysqli 时,您应该使用parameterized queries 和bind_param 将用户数据添加到您的查询中。 请勿使用字符串插值或连接来完成此操作,因为您创建了严重的SQL injection bug。 切勿$_POST$_GET任何用户数据直接放入查询中,如果有人试图利用您的错误,这可能会非常有害。 尝试将s.status_id = (改为s.status_id in ( 进行差异诊断:您是否尝试过直接在引擎上运行查询? (不是Heidi,不是Navicat,没有可以无缝偷工减料的DB管理软件……登录服务器控制台直接在上面运行查询?)。第二个差异:在这种情况下并不完全需要,但是您是否尝试过给子查询起别名?有时它会导致问题 【参考方案1】:

好吧,我刚刚发现了“错误”的原因。事实上,这根本不是错误,只是我很笨。

在 PHP 代码中,我更新了一行以将 excluido 设置为 NOW(),因此选定的行之一被软删除,这导致查询返回 0 行,但由于自动提交已变为 FALSE,并且通过当我转储查询时,我没有提交查询,更新被回滚,我在 Navicat 上运行查询,没有任何软删除的行,这就是它起作用的原因。现在,在调试前提交查询,该行被软删除,Navicat/Heidi 也返回 0 行。

我仍然没有弄清楚为什么它返回 0 行,但我认为这个问题属于另一个问题,而不是这个问题,所以如果我无法解决问题,我将提出一个新问题我。我只是在回答我自己的问题以关闭此线程,而不是浪费您的宝贵时间。

感谢所有在 cmets 中试图帮助我的人。

【讨论】:

以上是关于PHP mysqli 子查询返回 Impossible WHERE 在读取 const 表后注意到的主要内容,如果未能解决你的问题,请参考以下文章

在 PHP 中,为啥 mysqli_num_rows 不为返回行数为 0 的查询返回整数?

PHP小知识总结

mysqli 查询不返回 JSON 和谷歌图表的结果

mysqli 更新查询是不是应该返回结果?

php中mysqli 处理查询结果集的几个方法

从 mysqli 查询返回数组的多行