1

我正在尝试从 SQL 查询的结果中获取随机行。

我的查询如下

SET @rank = 0;
SELECT * FROM(
     SELECT (@rank:=@rank+1) AS num, ...
     FROM ...
     WHERE ....) as raw
WHERE raw.num = FLOOR(1 + (RAND() * @rank)) LIMIT 1

一般的想法是内部查询结果中的表的每一行都被赋予一个唯一的数字(num)。我已经手动检查确实是这种情况,并且每一行都有编号。

最后一行让我很痛苦。就目前而言,WHERE num = FLOOR(1 + (RAND() * @rank)) LIMIT 1正在返回我想要的 - 只有一半的时间。它似乎返回正确范围内的随机行(例如,我正在测试查询是 0-1299)。但是,每三个查询中就有一个绝对不会返回任何内容。

好的,所以我认为这可能是一个双精度问题,所以我尝试使用>=如下WHERE num >= FLOOR(1 + (RAND() * @rank)) LIMIT 1:这种情况下的结果让我感到困惑。使用此代码,我总是得到一个结果,但返回的行数总是 < 100。

因此,如果我们调用 FLOOR(1 + (RAND() * @rank)) x。当我使用=而不是>=它确认x必须(在某些情况下)等于大于 1000 的数字时。但是,当使用 时>=,满足条件的事实意味着 x 必须始终小于 100?

这是怎么回事?或者我还能如何解决我的问题

4

2 回答 2

5

我认为问题在于RAND()查询中的函数被多次调用,对于从raw. 如果发生了这种情况,那么它可能找不到任何满足谓词的行,因为它将每一行与不同的目标进行比较。(第一排是第五排吗?第二排是第三排吗?等等)

我会将对 RAND() 的调用和 @rank 的初始分配移到查询的开头,如下所示:

SELECT * FROM(
     SELECT (@rank:=@rank+1) AS num, ...
     FROM (SELECT @rand := RAND(), @rank := 0) r
     CROSS JOIN ...
     WHERE ....) as raw
WHERE raw.num = FLOOR(1 + @rand * @rank) LIMIT 1

-- 或者,根据您使用单独 SET 语句的模式 --

SET @rand = RAND();
SET @rank = 0;
SELECT * FROM(
     SELECT (@rank:=@rank+1) AS num, ...
     FROM ...
     WHERE ....) as raw
WHERE raw.num = FLOOR(1 + @rand * @rank) LIMIT 1

(我碰巧更喜欢前者,因为它作为单个语句运行;它不依赖于在 SELECT 语句之外设置的用户变量。)

但是其中任何一个都应该确保对RAND()函数的调用只发生一次(在查询开始时)。

除此之外,我对您所看到的行为没有很好的解释。

于 2012-07-09T20:46:38.003 回答
0

为什么不直接使用?:

ORDER BY RAND() LIMIT 1

如果没有,您可能想阅读: http ://akinas.com/pages/en/blog/mysql_random_row/

编辑 测试了您的查询,我得到了这个 udd 输出:

mysql> SET @rank = 0;
Query OK, 0 rows affected (0.00 sec)

mysql> SELECT * FROM(
    ->     SELECT (@rank:=@rank+1) AS num, id
    ->     FROM (
    ->         SELECT 1 AS id UNION SELECT 2 UNION SELECT 3
    ->         ) as abc
    ->     ) as raw
    -> WHERE raw.num = @rownr := FLOOR(1 + (RAND() * @rank)) LIMIT 1;
Empty set (0.00 sec)

mysql> SELECT @rownr;
+--------+
| @rownr |
+--------+
|      1 |
+--------+
1 row in set (0.00 sec)
于 2012-07-09T19:35:23.117 回答