有没有更好的方法来获得限制1的随机记录

时间:2016-05-18 04:48:56

标签: php mysql random

是否有更好的方法来获取限制为1的随机记录

我正在使用以下查询

$r = mysql_query("SELECT username FROM user ORDER BY RAND() LIMIT 1");

但是这给我造成了一个可怕的瓶颈(我的系统几乎慢了下来,几乎被绞死了)。有没有更好的选择呢。

5 个答案:

答案 0 :(得分:2)

你是对的,随机排序一个表可能会非常慢。

更快的解决方案是做类似的事情:

SELECT * FROM user WHERE id >= (SELECT FLOOR(MAX(id) * RAND()) FROM user) ORDER BY id LIMIT 1;

这种方法的缺点是如果id不均匀分布,结果不一定基于数据得到很好的平衡。

答案 1 :(得分:1)

这应该做这项工作:

SELECT username FROM user where ID =
  (SELECT ID FROM user ORDER BY RAND() LIMIT 1)

进一步深入。

@ JuliePelletier的解决方案太好了,但有些事情需要解决。

SELECT * FROM user WHERE id >= (SELECT FLOOR(MAX(id) * RAND()) FROM user) ORDER BY id LIMIT 1;  
  • 需要将FLOOR替换为CEIL以选择最后一位用户。甚至删除它,因为我们在这里使用>=
  • MAX(id)是一个惊人的优化调用(N(1)),但是如果我们将它与RAND()相乘,则会失去优化。 SELECT CEIL(RAND() * (SELECT MAX(ID)))修复了它。
  • 当我们寻找正确的用户时,
  • WHERE id>= X会针对每一行执行,因此相对较重的randceil函数会多次调用。它也与我的查询有关。

我们需要找一些查询,以确保我们只执行一次rand() 试试吧:

SELECT username
FROM users AS u1
  JOIN (SELECT (RAND() * (SELECT MAX(id) FROM random)) AS id) u2
WHERE u1.id >= u2.id
ORDER BY u1.id ASC
LIMIT 1;

此查询已优化但结果不平衡 示例:

  • 我们已经在表格中跟踪了ID:1, 100
  • 查询将返回99个100中的用户#100。

要解决此问题,我们可以在维护时运行ID的规范化,或者使用已存在的用户ID创建具有映射序列号的其他表。

如果您很少删除用户,则上一个解决方案效果很好:

create table user_map (
  id int not NULL primary key,
  user_id int not null
);
SET @i = 0;
INSERT INTO user_map SELECT @i := @i + 1, ID FROM users;

现在我们有了序列ID,可以选择平衡随机用户:

SELECT username FROM users u
JOIN (
    SELECT r1.user_id FROM user_map AS r1
    JOIN (
        SELECT (RAND() * (SELECT MAX(row_id) FROM user_map)) AS id
    ) AS r2
    WHERE r1.id >= r2.row_id
    ORDER BY r1.row_id ASC
    LIMIT 1
) as rows ON (u.id = rows.user_id);

我们现在需要的是当新用户出现时向user_map添加新记录,并且(重度任务)在用户删除时重建它。它可以通过触发器进行设置。

  • ORDER BY RAND()O(Nlog(N)) [如果仅扫描索引,则为小常量,但仍为nlogn]
  • 最后一个方法靠近O(1)

答案 2 :(得分:1)

我试过这个让我有更好的表现

$r = mysql_query("SELECT count(*) FROM user");
$d = mysql_fetch_row($r);
$rand = mt_rand(0,$d[0] - 1);

$r = mysql_query("SELECT username FROM user LIMIT $rand, 1");

答案 3 :(得分:0)

试试这个,

    SELECT 
        username 
    FROM 
        user 
    WHERE
        id = ( 
              SELECT ROUND((RAND() * ( MAX(id)-MIN(id) ))+ MIN(id)) 
              )    ; 

< SELECT ROUND((RAND()*(MAX(id)-MIN(id)))+ MIN(id))>会给你MAX(id)和MIN(id)之间的表中的随机ID。

我认为,你有< id>字段,带有索引。

答案 4 :(得分:0)

您可以在WHERE子句中使用随机数。它会比ORDER BY快得多。像这样:

SELECT username
FROM user
WHERE id >= RAND() * (SELECT MAX(id) FROM user)
ORDER BY id
LIMIT 1