是否有更好的方法来获取限制为1的随机记录
我正在使用以下查询
$r = mysql_query("SELECT username FROM user ORDER BY RAND() LIMIT 1");
但是这给我造成了一个可怕的瓶颈(我的系统几乎慢了下来,几乎被绞死了)。有没有更好的选择呢。
答案 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
会针对每一行执行,因此相对较重的rand
和ceil
函数会多次调用。它也与我的查询有关。 我们需要找一些查询,以确保我们只执行一次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;
此查询已优化但结果不平衡 示例:
1, 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] 答案 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