Answers:
一个出色的职位,可以处理多种情况,从简单到有缺口,再到有缺口的不均匀。
http://jan.kneschke.de/projects/mysql/order-by-rand/
对于大多数一般情况,这是您的操作方法:
SELECT name
FROM random AS r1 JOIN
(SELECT CEIL(RAND() *
(SELECT MAX(id)
FROM random)) AS id)
AS r2
WHERE r1.id >= r2.id
ORDER BY r1.id ASC
LIMIT 1
这假设id的分布是相等的,并且id列表中可能存在间隙。请参阅文章以获取更多高级示例
mysqli_fetch_assoc($result)
?还是那10个结果不一定可区分?
SELECT column FROM table
ORDER BY RAND()
LIMIT 10
不是有效的解决方案,但有效
ORDER BY RAND()
相对较慢
SELECT words, transcription, translation, sound FROM vocabulary WHERE menu_id=$menuId ORDER BY RAND() LIMIT 10
取0.0010,没有LIMIT 10取0.0012(在该表中为3500个字)。
简单查询,具有出色的性能并且可以弥补空白:
SELECT * FROM tbl AS t1 JOIN (SELECT id FROM tbl ORDER BY RAND() LIMIT 10) as t2 ON t1.id=t2.id
此查询在200K表上花费0.08s,而普通版本(SELECT * FROM tbl ORDER BY RAND()LIMIT 10)花费0.35s我的计算机上。
这是快速的,因为排序阶段仅使用索引ID列。您可以在说明中看到此行为:
SELECT * FROM tbl OR BY BY RAND()限制10:
SELECT * FROM tbl AS作为t1 JOIN(SELECT id from tbl ORDER BY RAND()LIMIT 10)as t2 ON t1.id = t2.id
我正在使用缓慢的cpu进行快速查询(大约0.5秒),在400K寄存器中选择10个随机行,而MySQL数据库的非缓存大小为2Gb。参见我的代码:快速选择MySQL中的随机行
<?php
$time= microtime_float();
$sql='SELECT COUNT(*) FROM pages';
$rquery= BD_Ejecutar($sql);
list($num_records)=mysql_fetch_row($rquery);
mysql_free_result($rquery);
$sql="SELECT id FROM pages WHERE RAND()*$num_records<20
ORDER BY RAND() LIMIT 0,10";
$rquery= BD_Ejecutar($sql);
while(list($id)=mysql_fetch_row($rquery)){
if($id_in) $id_in.=",$id";
else $id_in="$id";
}
mysql_free_result($rquery);
$sql="SELECT id,url FROM pages WHERE id IN($id_in)";
$rquery= BD_Ejecutar($sql);
while(list($id,$url)=mysql_fetch_row($rquery)){
logger("$id, $url",1);
}
mysql_free_result($rquery);
$time= microtime_float()-$time;
logger("num_records=$num_records",1);
logger("$id_in",1);
logger("Time elapsed: <b>$time segundos</b>",1);
?>
ORDER BY RAND()
FLUSH STATUS; SELECT ...; SHOW SESSION STATUS LIKE 'Handler%';
才能看到它。
ORDER BY RAND()
方法的好处是,它仅对id排序(而不是全行),因此temp表较小,但仍必须对所有id进行排序。
它非常简单和单行查询。
SELECT * FROM Table_Name ORDER BY RAND() LIMIT 0,10;
order by rand()
如果桌子很大,速度很慢
从书中:
使用偏移量选择随机行
避免上述替代方法中出现的问题的另一种技术是对数据集中的行进行计数,并返回一个介于0和该计数之间的随机数。然后在查询数据集时将此数字用作偏移量
<?php
$rand = "SELECT ROUND(RAND() * (SELECT COUNT(*) FROM Bugs))";
$offset = $pdo->query($rand)->fetch(PDO::FETCH_ASSOC);
$sql = "SELECT * FROM Bugs LIMIT 1 OFFSET :offset";
$stmt = $pdo->prepare($sql);
$stmt->execute( $offset );
$rand_bug = $stmt->fetch();
当您不能假设连续的键值并且需要确保每一行都有被选中的机会时,请使用此解决方案。
SELECT count(*)
速度变慢。
如何从表中选择随机行:
从这里开始: 在MySQL中选择随机行
对“表扫描”的快速改进是使用索引来拾取随机ID。
SELECT *
FROM random, (
SELECT id AS sid
FROM random
ORDER BY RAND( )
LIMIT 10
) tmp
WHERE random.id = tmp.sid;
PRIMARY KEY
)。
好吧,如果您的键中没有空格并且它们都是数字,则可以计算随机数并选择那些行。但这可能并非如此。
因此,一种解决方案是:
SELECT * FROM table WHERE key >= FLOOR(RAND()*MAX(id)) LIMIT 1
这基本上可以确保您在密钥范围内得到一个随机数,然后选择下一个更大的最佳数字。您必须执行10次。
但是,这并不是真正随机的,因为您的密钥很可能不会平均分配。
这确实是一个大问题,要满足所有要求并不容易,MySQL的rand()是您真正想要的最好的10个随机行。
但是,还有另一种快速的解决方案,但在随机性方面也要取舍,但可能会更适合您。在这里阅读:如何优化MySQL的ORDER BY RAND()函数?
问题是您需要随机性如何。
您能再解释一下吗,我可以给您一个很好的解决方案。
例如,与我合作的一家公司提供了一个解决方案,其中他们需要极快的绝对随机性。最后,他们使用从大到小的随机值预填充数据库,然后将其随机设置为不同的随机值。
如果您几乎从未更新过,也可以填写一个递增的id,这样就不会有任何空白,并且可以在选择之前计算随机密钥...这取决于用例!
Id
,您所有的随机查询都将返回该字符串Id
。
FLOOR(RAND()*MAX(id))
倾向于返回更大的ID。
我需要一个查询来从一个相当大的表中返回大量随机行。这就是我想出的。首先获取最大记录ID:
SELECT MAX(id) FROM table_name;
然后将该值代入:
SELECT * FROM table_name WHERE id > FLOOR(RAND() * max) LIMIT n;
其中max是表中的最大记录ID,n是您想要在结果集中的行数。假设记录id中没有空格,尽管我怀疑如果有的话会影响结果(虽然没有尝试过)。我还创建了这个存储过程,使其更加通用。传入表名和要返回的行数。我在Windows 2008、32GB,双3GHz E5450上运行MySQL 5.5.38,在具有17,361,264行的表上,在〜.03秒/〜11秒的时间内返回100万行的时间是相当一致的。(时间来自MySQL Workbench 6.1;您也可以根据自己的喜好在第二个选择语句中使用CEIL代替FLOOR)
DELIMITER $$
USE [schema name] $$
DROP PROCEDURE IF EXISTS `random_rows` $$
CREATE PROCEDURE `random_rows`(IN tab_name VARCHAR(64), IN num_rows INT)
BEGIN
SET @t = CONCAT('SET @max=(SELECT MAX(id) FROM ',tab_name,')');
PREPARE stmt FROM @t;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET @t = CONCAT(
'SELECT * FROM ',
tab_name,
' WHERE id>FLOOR(RAND()*@max) LIMIT ',
num_rows);
PREPARE stmt FROM @t;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END
$$
然后
CALL [schema name].random_rows([table name], n);
我改进了@Riedsio的答案。这是我可以在带有间隙的大型,均匀分布的表上找到的最有效的查询(测试是从具有大于2.6B行的表中获取1000个随机行)。
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max := (SELECT MAX(id) FROM table)) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1)
让我解压缩发生的事情。
@max := (SELECT MAX(id) FROM table)
MAX(id)
每次需要一行时进行计算都会有一些开销SELECT FLOOR(rand() * @max) + 1 as rand)
SELECT id FROM table INNER JOIN (...) on id > rand LIMIT 1
进行并集可帮助您将所有内容放入1个查询中,从而避免执行多个查询。它还可以节省计算的开销MAX(id)
。根据您的应用程序,这可能很重要,也可能很小。
请注意,这只会获取ID并以随机顺序获取它们。如果您想做任何更高级的事情,我建议您这样做:
SELECT t.id, t.name -- etc, etc
FROM table t
INNER JOIN (
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max := (SELECT MAX(id) FROM table)) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1)
) x ON x.id = t.id
ORDER BY t.id
LIMIT 1
,以LIMIT 30
无处不在的查询
LIMIT 1
为LIMIT 30
可以从表中的随机点连续获取30条记录。您应该(SELECT id FROM ....
在中间放置30份该零件的副本。
Riedsio
回答更有效的方法。我已经在centos 7上使用PHP 7.0.22和MariaDB在页面上每秒命中500次,Riedsio
答案是我获得了500多个额外的成功响应,然后是您的答案。
我使用了Riedsio发布的http://jan.kneschke.de/projects/mysql/order-by-rand/(我使用的是存储过程返回一个或多个随机值的情况):
DROP TEMPORARY TABLE IF EXISTS rands;
CREATE TEMPORARY TABLE rands ( rand_id INT );
loop_me: LOOP
IF cnt < 1 THEN
LEAVE loop_me;
END IF;
INSERT INTO rands
SELECT r1.id
FROM random AS r1 JOIN
(SELECT (RAND() *
(SELECT MAX(id)
FROM random)) AS id)
AS r2
WHERE r1.id >= r2.id
ORDER BY r1.id ASC
LIMIT 1;
SET cnt = cnt - 1;
END LOOP loop_me;
在文章中,他通过维护一个表(使用触发器等,请参见该文章)解决了id 差异导致的随机性问题。我正在通过向表中添加从1开始填充连续数字的另一列来解决该问题(编辑:此列将添加到由子查询在运行时创建的临时表中,不会影响您的永久表):
DROP TEMPORARY TABLE IF EXISTS rands;
CREATE TEMPORARY TABLE rands ( rand_id INT );
loop_me: LOOP
IF cnt < 1 THEN
LEAVE loop_me;
END IF;
SET @no_gaps_id := 0;
INSERT INTO rands
SELECT r1.id
FROM (SELECT id, @no_gaps_id := @no_gaps_id + 1 AS no_gaps_id FROM random) AS r1 JOIN
(SELECT (RAND() *
(SELECT COUNT(*)
FROM random)) AS id)
AS r2
WHERE r1.no_gaps_id >= r2.id
ORDER BY r1.no_gaps_id ASC
LIMIT 1;
SET cnt = cnt - 1;
END LOOP loop_me;
在文章中,我看到他竭尽全力优化代码。我没有想法是否/我的更改会影响性能多少,但对我来说效果很好。
@no_gaps_id
可以使用no索引,因此与原始查询相比,如果您查找EXPLAIN
查询,则子查询具有Using filesort
和Using where
(不带索引)。
这是一个游戏规则改变者,可能对许多人有帮助。
我有一个具有200k行的表,具有顺序ID,我需要选择N个随机行,因此我选择根据表中的最大ID生成随机值,我创建了此脚本来找出最快的操作:
logTime();
query("SELECT COUNT(id) FROM tbl");
logTime();
query("SELECT MAX(id) FROM tbl");
logTime();
query("SELECT id FROM tbl ORDER BY id DESC LIMIT 1");
logTime();
结果是:
36.8418693542479
毫秒0.241041183472
毫秒0.216960906982
毫秒根据此结果,order desc是获取最大id的最快操作,
这是我对问题的回答:
SELECT GROUP_CONCAT(n SEPARATOR ',') g FROM (
SELECT FLOOR(RAND() * (
SELECT id FROM tbl ORDER BY id DESC LIMIT 1
)) n FROM tbl LIMIT 10) a
...
SELECT * FROM tbl WHERE id IN ($result);
仅供参考:要从200k表中获取10条随机行,我花了1.78 毫秒 (包括php端的所有操作)
LIMIT
稍微增加一点-可以获得重复项。
所有最佳答案已经发布(主要是那些引用链接http://jan.kneschke.de/projects/mysql/order-by-rand/的答案)。
我想指出另一个提速的可能性- 缓存。想一想为什么需要获得随机行。可能您想在网站上显示一些随机帖子或随机广告。如果您获得100 req / s,是否真的需要每个访问者都获得随机行?通常,将这X个随机行缓存1秒(甚至10秒)是完全可以的。相同的1秒钟内100位唯一身份访问者是否获得相同的随机帖子并不重要,因为下一秒内另外100位访客将获得不同的帖子集。
使用这种缓存时,您还可以使用一些较慢的解决方案来获取随机数据,因为无论您的要求如何,每秒仅从MySQL提取一次随机数据。
这是超快的,即使有差距,也是100%随机的。
x
可用的行数SELECT COUNT(*) as rows FROM TABLE
a_1,a_2,...,a_10
从0到10 之间挑选10个不同的随机数x
SELECT * FROM TABLE LIMIT 1 offset a_i
对于i = 1,...,10我发现这个黑客在书SQL反模式从比尔Karwin。
SELECT column FROM table ORDER BY RAND() LIMIT 10
在O(nlog(n))中。是的,这是斋戒的解决方案,适用于任何ID分配。
x
。我认为这不是10行的随机生成。在我的答案中,您必须在步骤3中执行查询10次,即每次执行仅获得一行,并且不必担心偏移量是否位于表的末尾。
将@redsio的答案与临时表结合起来(600K并不多):
DROP TEMPORARY TABLE IF EXISTS tmp_randorder;
CREATE TABLE tmp_randorder (id int(11) not null auto_increment primary key, data_id int(11));
INSERT INTO tmp_randorder (data_id) select id from datatable;
然后采用@redsios版本:
SELECT dt.*
FROM
(SELECT (RAND() *
(SELECT MAX(id)
FROM tmp_randorder)) AS id)
AS rnd
INNER JOIN tmp_randorder rndo on rndo.id between rnd.id - 10 and rnd.id + 10
INNER JOIN datatable AS dt on dt.id = rndo.data_id
ORDER BY abs(rndo.id - rnd.id)
LIMIT 1;
如果桌子很大,您可以在第一部分进行筛选:
INSERT INTO tmp_randorder (data_id) select id from datatable where rand() < 0.01;
版本:您可以保持表的tmp_randorder
持久性,将其称为datatable_idlist。请按一定的时间间隔(天,小时)重新创建该表,因为它也会出现孔。如果桌子真的很大,您也可以补上孔
从datatable_idlist中整体选择l.data_id l在dt.id = l.data_id上左连接数据表dt,其中dt.id为null;
版本:直接在数据表或持久性额外表中为您的数据集提供random_sortorder列datatable_sortorder
。索引该列。在您的应用程序中生成一个随机值(我将其称为$rand
)。
select l.*
from datatable l
order by abs(random_sortorder - $rand) desc
limit 1;
此解决方案可区分具有最高和最低random_sortorder的“边缘行”,因此应按间隔(一天一次)重新排列它们。
另一个简单的解决方案是对行进行排名并随机获取其中的一行,使用此解决方案,您无需在表中具有任何基于“ Id”的列。
SELECT d.* FROM (
SELECT t.*, @rownum := @rownum + 1 AS rank
FROM mytable AS t,
(SELECT @rownum := 0) AS r,
(SELECT @cnt := (SELECT RAND() * (SELECT COUNT(*) FROM mytable))) AS n
) d WHERE rank >= @cnt LIMIT 10;
您可以根据需要更改限制值,以访问所需的任意多行,但这多数是连续值。
但是,如果您不希望连续的随机值,则可以获取更大的样本并从中随机选择。就像是 ...
SELECT * FROM (
SELECT d.* FROM (
SELECT c.*, @rownum := @rownum + 1 AS rank
FROM buildbrain.`commits` AS c,
(SELECT @rownum := 0) AS r,
(SELECT @cnt := (SELECT RAND() * (SELECT COUNT(*) FROM buildbrain.`commits`))) AS rnd
) d
WHERE rank >= @cnt LIMIT 10000
) t ORDER BY RAND() LIMIT 10;
如果有一个自动生成的ID,我觉得很好的一种方法是使用模运算符'%'。例如,如果您需要70,000条中的10,000条随机记录,则可以通过说每7行中需要1条来简化此过程。此查询可以简化此操作:
SELECT * FROM
table
WHERE
id %
FLOOR(
(SELECT count(1) FROM table)
/ 10000
) = 0;
如果将目标行除以可用总数的结果不是整数,则将比您要求的行多一些,因此您应该添加LIMIT子句来帮助您修剪结果集,如下所示:
SELECT * FROM
table
WHERE
id %
FLOOR(
(SELECT count(1) FROM table)
/ 10000
) = 0
LIMIT 10000;
这确实需要进行全面扫描,但比ORDER BY RAND要快,而且我认为比该线程中提到的其他选项更容易理解。同样,如果写入数据库的系统成批创建行集,那么您可能不会获得预期的随机结果。
如果您想要一个随机记录(无论id之间是否有间隔):
PREPARE stmt FROM 'SELECT * FROM `table_name` LIMIT 1 OFFSET ?';
SET @count = (SELECT
FLOOR(RAND() * COUNT(*))
FROM `table_name`);
EXECUTE stmt USING @count;
来源:https : //www.warpconduit.net/2011/03/23/selecting-a-random-record-using-mysql-benchmark-results/#comment-1266
我已经仔细研究了所有答案,但我认为根本没有人提到这种可能性,而且我不确定为什么。
如果您想以最小的成本获得最大的简便性和速度,那么对我来说,在数据库中针对每一行存储一个随机数似乎是有意义的。只需创建一个额外的列,random_number
并将其默认设置为即可RAND()
。在此列上创建索引。
然后,当您要检索行时,在代码中生成一个随机数(PHP,Perl等)并将其与列进行比较。
SELECT FROM tbl WHERE random_number >= :random LIMIT 1
我想虽然单行非常整洁,但是对于10行(如OP),您必须将其单独调用十次(或者想出一个巧妙的调整方法,使我立即逃脱)
PREPARE stm from 'select * from table limit 10 offset ?';
SET @total = (select count(*) from table);
SET @_offset = FLOOR(RAND() * @total);
EXECUTE stm using @_offset;
您也可以像这样应用where子句
PREPARE stm from 'select * from table where available=true limit 10 offset ?';
SET @total = (select count(*) from table where available=true);
SET @_offset = FLOOR(RAND() * @total);
EXECUTE stm using @_offset;
对600,000行(700MB)的表进行了测试,查询执行花费了约0.016秒的硬盘驱动器
--EDIT--
偏移量可能接近表末尾的值,这将导致select语句返回较少的行(或者可能仅返回1行),为避免这种情况,我们可以offset
在声明后再次检查,就像这样
SET @rows_count = 10;
PREPARE stm from "select * from table where available=true limit ? offset ?";
SET @total = (select count(*) from table where available=true);
SET @_offset = FLOOR(RAND() * @total);
SET @_offset = (SELECT IF(@total-@_offset<@rows_count,@_offset-@rows_count,@_offset));
SET @_offset = (SELECT IF(@_offset<0,0,@_offset));
EXECUTE stm using @rows_count,@_offset;
我使用以下查询:
select floor(RAND() * (SELECT MAX(key) FROM table)) from table limit 10
查询时间:0.016s
使用下面的简单查询从表中获取随机数据。
SELECT user_firstname ,
COUNT(DISTINCT usr_fk_id) cnt
FROM userdetails
GROUP BY usr_fk_id
ORDER BY cnt ASC
LIMIT 10
我想这是最好的方法。
SELECT id, id * RAND( ) AS random_no, first_name, last_name
FROM user
ORDER BY random_no