从mysql数据库返回随机行而不使用rand()

时间:2010-08-03 15:07:38

标签: php mysql random

我希望能够从数据库中提取15个左右的记录。我已经看到,随着我的数据库变大,使用WHERE id = rand()会导致性能问题。我见过的所有解决方案都是为了选择单个随机记录。我想得到倍数。

有没有人知道为大型数据库执行此操作的有效方法?

编辑:

进一步编辑和测试:

我在使用MyISAM的新数据库上创建了一个相当简单的表。我给出了这3个字段:autokey(无符号自动数字键)bigdata(大blob)和somemore(中等int)。 然后我将随机数据应用到表中并使用Navicat运行一系列查询。结果如下:

Query 1: select * from test order by rand() limit 15

 Query 2: select * 
          from 
      test 
          join 
      (select round(rand()*(select max(autokey) from test)) as val from test limit 15)                                           as rnd
      on 
          rnd.val=test.autokey;`

(我尝试选择并选择不同,并且没有明显区别)

Query 3 (I only ran this on the second test):
SELECT  *
    FROM    (
    SELECT  @cnt := COUNT(*) + 1,
            @lim := 10
    FROM    test
    ) vars
    STRAIGHT_JOIN
    (
    SELECT  r.*,
            @lim := @lim - 1
    FROM    test r
    WHERE   (@cnt := @cnt - 1)
            AND RAND(20090301) < @lim / @cnt
    ) i
ROWS:            QUERY 1:               QUERY 2:         QUERY 3:
2,060,922          2.977s                 0.002s            N/A

3,043,406          5.334s                 0.001s            1.260     

我想做更多的行,所以我可以看到查询3如何缩放,但目前,似乎明显的赢家是查询2

在我完成此测试并宣布答案之前,当我设置了所有这些数据和测试环境时,有人可以推荐任何进一步的测试吗?

5 个答案:

答案 0 :(得分:5)

尝试:

select * from table order by rand() limit 15

另一种(可能更有效的方式)是加入一组随机值。如果表中有一些连续的整数键,这应该可以工作。这是我在 postgres 中做的事情(我的MySQL有点生疏)

select * from table join 
   (select (random()*maxid)::integer as val from generate_series(1,15)) as rnd
   on rand.val=table.id;

其中maxid是id中的最高table。如果id有一个索引,那么这意味着只有15个索引查找,所以它非常快。

<强>更新

在MySQL中看起来没有generate_series这样的东西。我的错。我们实际上并不需要它:

select * 
from 
 table 
join 
 -- this just returns 15 random numbers. 
 -- I need `table` here only to produce rows for rand()
 (select round(rand()*(select max(id) from table)) as val from table limit 15) as rnd
on 
 rnd.val=table.id;

P.S。如果我不想返回重复项,我可以在随机生成器表达式中使用(选择distinct [...])。

答案 1 :(得分:2)

  

更新:在this question中查看已接受的答案。它是纯粹的mySQL,甚至可以处理均匀分布。

id = rand()或PHP中可比较的问题是您无法确定该特定ID是否仍然存在。因此,您需要使用LIMIT,这对于大量数据来说可能会变慢。

作为替代方案,您可以尝试在PHP中使用循环。

循环的作用是

  • 使用rand()创建一个随机整数,范围介于0和数据库中的记录数

  • 查询数据库是否存在具有该ID的记录

  • 如果存在,请将数字添加到数组

  • 如果没有,请返回步骤1

  • 当随机数组包含所需数量的元素

  • 时结束循环

此方法可能会在碎片表中导致大量查询,但执行起来应该非常快。在某些情况下,可能LIMIT rand()更快。

@Luther概述的LIMIT方法无疑是最简单的代码。

答案 2 :(得分:0)

您可以使用所有结果进行查询,或者使用有限的结果进行查询,然后使用mysqli_fetch_all,然后使用:

shuffle($a);
$a = array_slice($a, 0, 15);

答案 3 :(得分:0)

对于执行

的大型数据集
select * from table order by rand() limit 15

可能耗费时间和内存。

如果您的数据记录恰好编号,您可以在编号列上放置并编制索引并执行

select * from table where no >= rand() limit 15

甚至可以更好地在应用程序中生成随机数并执行

select * from table where no >= $rand and no <= $rand+15

如果您的数据不会经常更改,则可能需要添加这样的编号列以使选择更有效。

答案 4 :(得分:0)

假设MySQL支持嵌套查询并且主键上的操作很快,我会尝试类似

的内容
select * from table where id in (select id from table order by rand() limit 15)