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 表后注意到的主要内容,如果未能解决你的问题,请参考以下文章