PHP MySQL随机选择两行,但不使用rand()


PHP MySQL select two random rows but not with rand()

我需要选择2个随机行,但众所周知rand()太慢了。所以我尝试了一个网站上的代码,它是:

SELECT *
  FROM bilder AS r1 JOIN
       (SELECT (RAND() *
                     (SELECT MAX(id)
                        FROM bilder)) AS id)
        AS r2
 WHERE r1.id >= r2.id
 ORDER BY r1.id ASC
 LIMIT 2

但这样我多次得到相同的2行,解析也不正确,所以这完全没有用。有比rand()更好的有效解决方案吗?表名为bilder,字段为:iduseridnickname。CCD_ 5是主要的和自动递增的。一些行也被删除了,所以不是1 2 3 4 5,而是1 2 4 5 6…所以生成随机数并选择它们的解决方案不起作用

这个问题有多种解决方案,但像以下这样的解决方案通常具有足够好的性能:

SELECT b.*
FROM bilder b CROSS JOIN
     (SELECT COUNT(*) as cnt FROM bilder) v
WHERE rand() <= 100 / cnt
ORDER BY rand()
LIMIT 2;

子查询选择大约100行。对如此少量的行进行排序通常非常快。然后选择其中两个。

最有可能导致您惊愕的原因是未能在对CEIL()的调用中包装RAND() * (SELECT MAX(id) FROM bilder),导致浮点值而不是整数:

 SELECT *
  FROM bilder AS r1 JOIN
       (SELECT ceil(RAND() *
                     (SELECT MAX(id)
                        FROM bilder)) AS id)
        AS r2
 WHERE r1.id >= r2.id
 ORDER BY r1.id ASC
 LIMIT 2

选择一个随机行的方法要快得多。下面这两种方法都只选择一个随机行。你要求随机两排。但这些方法比对表扫描快几个数量级,因此即使需要多次尝试才能获得第二个不同的随机行,也值得使用这些方法。

最快的方法是在两个查询中完成(我将在伪代码中显示):

$max = SELECT MAX(id) FROM bilder
$rand1 = rand(1..$max)-1
SELECT * FROM bilder WHERE id > $rand1 LIMIT 1
$id1 = id of the first row chosen
$rand2 = rand(1..$max)-1
SELECT * FROM bilder WHERE id > $rand2 AND id <> $id1 LIMIT 1
$id2 = id of the second row chosen
if $id2 = $id1, then choose a new $rand2 and query again

这样做的问题是,如果由于删除的行而出现较大的间隙,则选择间隙后面的行的机会会更高。

如果不经常更新表,另一种快速方法是添加一列进行连续排序,然后按随机顺序为该列分配顺序值:

ALTER TABLE bilder ADD COLUMN rank INT UNSIGNED, ADD KEY (rank);
SET @r := 0;
UPDATE bilder SET rank = (@r:=@r+1) ORDER BY RAND();

做一次这个排名。它会很慢。然后,一旦对行进行了排序,您就可以快速选择随机值:

$max = SELECT MAX(rank) FROM bilder;
$rand1 = rand(1..$max)
$rand2 = rand(1..$max) until $rand2 != $rand1
SELECT * FROM bilder WHERE rank IN ($rand1, $rand2);

当然,如果在表中添加或删除任何行,则必须对这些行重新编号。或者至少你可以更有效地做到这一点:

  • 如果插入,则插入具有随机值的新行,并将现有行的秩更新为$max+1
  • 如果删除,请记下已删除行的级别,并将级别为$max的行更新为刚刚删除的级别