WHERE子句中的MySQL RAND()匹配一小组行

时间:2018-02-08 13:58:43

标签: php mysql sql random

我遇到了一个有趣的MySQL问题。当我尝试将RAND()函数与一个大整数相乘时,我得到的最大随机数非常小。这是我的MySQL查询,应该是一个非常快速的随机查询,但它返回ID最大36000,即使有4600000+ ID。

SET @maxID=(SELECT MAX(id) FROM property); #it's about 4600000

SELECT * FROM property
WHERE 
downloaded_at IS NULL
AND id >= FLOOR(1 + RAND() * @maxID) #this returns max +/-36000
LIMIT 100

当我将此代码移动到普通SELECT查询时,一切都很好

SELECT FLOOR(1 + RAND() * (SELECT MAX(id) FROM property))

有人可以解释一下,为什么会出现这个错误?谢谢!

编辑

嗯,不知怎的,当我删除downloaded_at IS NULL时,ID会更高,但结果不再是随机的。

我不能使用ORDER BY RAND(),因为表太大,查询太慢,整个服务器最终在几分钟内崩溃

版本是5.7.21-0ubuntu0.16.04.1

2 个答案:

答案 0 :(得分:1)

您的随机行选择方法 偏向 ...正在选择的行的概率与其ID成正比。例如,如果你有10行id = 1到10,那么1有10%被选中的机会,2有20%等等。

此外,您的代码选择小于~36000的ID的原因显而易见:行(通常)以PK顺序处理,并且在找到第100个匹配行时,查询仅处理ID大约为36000的行。

现在,如果您有兴趣选择100个随机行,则可以改为使用此查询:

SELECT *
FROM property
WHERE id IN (
    SELECT id
    FROM property
    WHERE downloaded_at IS NULL
    ORDER BY RAND()
    LIMIT 100
)

或者可能是这个(粗略轮廓):

SELECT *
FROM property
WHERE id IN (
    SELECT id
    FROM property
    WHERE RAND() <= 100.0 / @maxID -- explanation below
    LIMIT 100
)

以上内容不涉及排序,但仍需要扫描所有ID。 100.0与所需的行数相同,但为了确保添加更多行。这应该导致每行选择的概率相等。

答案 1 :(得分:0)

问题是每次评估rand()子句中的条件时都会调用where。相反,将值放在子查询中:

SELECT p.*
FROM property p CROSS JOIN
     (SELECT FLOOR(1 + RAND() * @maxID) as idlim) x
WHERE p.downloaded_at IS NULL AND
      p.id >= x.idlim #this returns max +/-36000
LIMIT 100;

这确保了rand()函数只被调用一次。