MySQL RAND()奇怪的行为

时间:2012-07-09 19:29:32

标签: mysql sql database

我试图从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?

发生了什么事?或者我怎么解决我的问题

2 个答案:

答案 0 :(得分: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()函数的调用恰好发生一次(在查询开始时)。

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

答案 1 :(得分: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)