【问题标题】:PHP MySQL select two random rows but not with rand()PHP MySQL选择两个随机行但不使用rand()
【发布时间】:2014-10-14 21:22:02
【问题描述】:

我需要选择 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,字段为:iduseridnicknameid 是主要和自动增量。一些行也被删除,所以它不是 1 2 3 4 5 而是 1 2 4 5 6...所以生成随机数并选择它们的解决方案将不起作用

【问题讨论】:

标签: php mysql random


【解决方案1】:

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

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

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

【讨论】:

  • 好的,所以当我有 1000 行时,它会在第一个查询中选择 50 - 150 的 2 行,在第二个查询中选择 300 - 400 的 2 行等等?
  • @user2966167 。 . . where 子句将在数据中选择大约 100 个随机行(这是可控的,但 100 应该足以满足您的目的)。外部order by 然后从这个集合中选择。因此,尽管它们可能是相邻的 id,但通常不会发生这种情况。
【解决方案2】:

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

 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

【讨论】:

  • 这里是这个查询在大约 20 次运行中为我提供的 ID 对,使用的样本数据集 id 的平均差距为 3:((79, 82), (133, 136), (124, 127), (223, 226), (139, 142), (28, 31), (19, 22), (7, 10), (280, 283), (178, 181), (52, 55), (19, 22), (244, 247),(190, 193), (94, 97), (187, 190), (58, 61), (22, 25), (235, 238), (220, 223))
  • 我试过了,但是解析有问题,当我回显 id 时,我得到 1,1 4,4 ,3,3 等等:/
【解决方案3】:

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

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

$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 的行更新为您刚刚删除的排名。

【讨论】:

  • 最快的方法是我所做的,删除行存在问题。因此,使用 ALTER TABLE... 的解决方案将使 id 1 2 3 5 6 变为 1 2 3 4 5?
  • 不,您想保留现有的主键。我建议添加一个具有连续值的新列。因此,id 为 1 2 3 5 6 的行将具有随机排名,例如2 5 4 3 1.
猜你喜欢
  • 2013-04-23
  • 1970-01-01
  • 2011-09-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-07-26
相关资源
最近更新 更多