即使MYSQLi具有有效的SQL和PHP语法,它也不返回任何行

时间:2014-11-28 19:04:46

标签: php mysql mysqli

我在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的图像显示结果:

HeidiSQL results

有人有什么想法吗?

ax的列为IDUserBI。 在SQL中,它创建了两个名为“Entirety”的临时列,它是概率计数器和一个名为RandomValue的列,它是一个从0开始的rand() - > 1。

此表中唯一的行的值为(1,1,10)。即使它是10,它的Entirety为'1',这意味着它是100%保证被选中。尽管如此,没有选择任何东西。

3 个答案:

答案 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

来自我的客户:

第一次解答:

first time execution

第二次EXPLAIN:

second time

希望这有帮助。

答案 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查询可能有与非整数或负值相关的类似问题。)