我在HeidiSQL中运行的SQL代码一次又一次地运行,但是当我将它克隆到PHP并运行mysqli_query($ db,$ sql)时,它不起作用。
以下PHP / MySQL代码都是有效且完美的。
$sql = "select `ID`,`User` from (
select * from
(SELECT
`ID`,
`User`,
`BI`,
(@cnt:= @cnt + (`BI`/(select SUM(`BI`) from `ax`))) as `Entirety`
from `ax` as `t`
CROSS JOIN (SELECT @cnt := 0) AS var
order by `BI`
) d
where `Entirety`>(@rnd)
order by `BI`
) as `l`
cross join (select (@rnd := rand()) as `RandomValue`) as var2
limit 1;";
然后我通过
运行$ sql$result = mysqli_query($db,$sql);
$results = mysqli_fetch_array($result);
其中$ db是与MySQL服务器的有效且开放的连接。但是,当我这样做时,对象的返回
print_r($result);
出现
mysqli_result Object (
[current_field] => 0
[field_count] => 2
[lengths] =>
[num_rows] => 0
[type] => 0
)
我不想要这个,而且num_rows应该是'1',因为这是HeidiSQL运行时显示的内容。 这是HeidiSQL的图像显示结果:
有人有什么想法吗?
表ax
的列为ID
,User
和BI
。
在SQL中,它创建了两个名为“Entirety”的临时列,它是概率计数器和一个名为RandomValue
的列,它是一个从0开始的rand() - > 1。
此表中唯一的行的值为(1,1,10)。即使它是10,它的Entirety
为'1',这意味着它是100%保证被选中。尽管如此,没有选择任何东西。
答案 0 :(得分:8)
如果在PHP中将EXPLAIN添加到查询中,mysqli会告诉您一个子查询的错误:
Impossible WHERE noticed after reading const tables
和
const row not found
这可能意味着很多,但在这种情况下,我的赌注是列中的无效值。我很确定
select (@rnd := rand()) as `RandomValue`
在
之后执行where `Entirety`>(@rnd)
因此没有设定。
那么,为什么它适用于您的客户?因为客户端保持连接。因此,它在第一次执行查询时失败,但第二次它在第一次执行时具有@rnd的存储值。您可以通过在@ rnd1中重命名@rnd来测试它并运行一次查询。
因此,解决方案是(正如Matt指出的那样)先设置变量@rnd然后再使用它。
以下是关于子查询中用户定义变量的相关SO问题:
User variable in MySQL subquery
来自我的客户:
第一次解答:
第二次EXPLAIN:
希望这有帮助。
答案 1 :(得分:4)
这里很有意思:如果你颠倒了@rnd变量的交叉连接查询的顺序:
select `ID`,`User` from
(select (@rnd := rand()) as `RandomValue`) as var2 CROSS JOIN
(
select * from
(SELECT
`ID`,
`User`,
`BI`,
(@cnt:= @cnt + (`BI`/(select SUM(`BI`) from `ax`))) as `Entirety`
from `ax` as `t`
CROSS JOIN (SELECT @cnt := 0) AS var
order by `BI`
) d
where `Entirety`>(@rnd)
order by `BI`
) as `l`
limit 1;
...然后我相信这在逻辑上是相同的查询。但是,这个版本会返回您从PHP和标准MySQL客户端运行时所期望的行,而我可以用原始版本重现您缺少PHP的结果。
注意:我仍然无法告诉你为什么会发生这种情况。即使这在技术上回答了你的问题,我个人也不会将其标记为答案,并留下来吸引那些可能了解实际情况的人的其他答案,至少一两天。如果它在48小时后仍然开放,我会给它一个赏金,因为我很好奇。
答案 2 :(得分:2)
问题的关键在于@rnd
在评估时未初始化。它基本上是一个操作顺序问题。
该语句似乎在HeidiSQL中有效,因为该语句使用先前由先前执行的语句分配给@rnd
的值。 (我们知道MySQL用户定义的变量在查询执行期间保留其值,在会话期间,MySQL数据库连接)。
要使RAND()
评估一次,在语句执行的早期,我们可以将分配下移到最低级别的内联视图,该视图将首先进行评估。 (这是因为MySQL在外部查询运行之前评估内联视图查询以实现"派生表"的方式。
如果我保证BI
列包含正整数值,那么我更倾向于使用如下语句返回结果:
SELECT d.ID
, d.User
FROM ( SELECT t.ID
, t.User
, t.BI
, @rt := @rt + t.BI AS rt
FROM `ax` t
CROSS
JOIN ( SELECT @rt := 0
, @rnd := FLOOR(1 + RAND() * (r.tot – 1))
FROM ( SELECT SUM(q.BI) AS tot FROM `ax` q ) r
) s
ORDER BY t.BI
) d
WHERE d.rt > @rnd
ORDER BY d.BI
LIMIT 1
N.B。 MySQL参考手册指出,我们在语句中使用用户定义的变量观察到的行为不保证,不应该依赖它。
但我们确实观察到一致的行为,至少在MySQL 5.1和5.5中。 MySQL文档清楚地表明MySQL的未来版本可以自由地改变这种行为。
(如果BI
不是整数,或者它可以是零或负数,那么该语句用于确定"运行总数"(rt
)的方法可能不正常。我认为OP查询可能有与非整数或负值相关的类似问题。)