从mysql中的一个大表中快速select一个随机的行

什么是从大的MySQL表中select一个随机行的快速方法?

我正在使用PHP,但是即使使用其他语言,我也对任何解决scheme感兴趣。

抓住所有的ID,从中随机挑选一个,并检索完整的行。

如果你知道id是连续的没有漏洞,你可以抓住最大值并计算一个随机的id。

如果这里和那里都有漏洞,但大部分是顺序值,并且您不关心稍微偏斜的随机性,那么获取最大值,计算一个id,然后selectid等于或高于所计算值的第一行。 歪斜的原因是id后面的这个洞比另一个id更容易被挑选。

如果您随意订购,您的手上就会有可怕的桌面扫描, quick这个词不适用于这种解决scheme。

不要那么做,也不应该用GUID来订购,它也有同样的问题。

我知道必须以一种快速的方式在单个查询中做到这一点。 这里是:

没有外部代码参与的快速方式,荣誉

http://jan.kneschke.de/projects/mysql/order-by-rand/

SELECT name 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; 

MediaWiki使用一个有趣的技巧(对于维基百科的Special:Randomfunction):带有文章的表格有一个带有随机数字的额外列(在创build文章时生成)。 要得到一个随机的文章,产生一个随机数,并获得随机数字列中的下一个更大或更小(不记得哪个)值的文章。 有了索引,这可以是非常快的。 (MediaWiki是用PHP编写的,为MySQL开发的。)

如果结果数字分布不均匀,这种方法可能会导致问题; IIRC,这个问题已经在MediaWiki上解决了,所以如果你决定这样做的话,你应该看一下代码,看看它是如何完成的(可能他们周期性地重新生成随机数列)。

这是一个运行速度相当快的解决scheme,它可以获得更好的随机分布,而不依赖于id值是连续的还是从1开始。

 SET @r := (SELECT ROUND(RAND() * (SELECT COUNT(*) FROM mytable))); SET @sql := CONCAT('SELECT * FROM mytable LIMIT ', @r, ', 1'); PREPARE stmt1 FROM @sql; EXECUTE stmt1; 

将包含计算的随机值的列添加到每一行,并在sorting子句中使用该列,在select时限制为一个结果。 这比使用ORDER BY RANDOM()引起的表扫描更快。

更新:在发布SELECT语句之前,您仍然需要计算一些随机值,当然,例如

 SELECT * FROM `foo` WHERE `foo_rand` >= {some random value} LIMIT 1 

也许你可以做这样的事情:

 SELECT * FROM table WHERE id= (FLOOR(RAND() * (SELECT COUNT(*) FROM table) ) ); 

这是假设你的身份证号码是连续的,没有差距。

一个简单而缓慢的方法是(对小桌子很好)

 SELECT * from TABLE order by RAND() LIMIT 1 

在伪代码中:

 sql "select id from table" store result in list n = random(size of list) sql "select * from table where id=" + list[n] 

这假定id是一个唯一的(主键)。

还有另外一种方法可以只使用查询来生成随机行,而不需要rand()。 它涉及用户定义的variables。 了解如何从表中生成随机行

为了从表中find随机行,不要使用ORDER BY RAND(),因为它强制MySQL执行完整的文件sorting,然后才能检索所需的限制行数。 为了避免这种完整的文件分类,只能在where子句中使用RAND()函数。 只要达到所需的行数就会停止。 请参阅http://www.rndblog.com/how-to-select-random-rows-in-mysql/

如果你不在这个表中删除行,最有效的方法是:

(如果你知道mininum id只是跳过它)

 SELECT MIN(id) AS minId, MAX(id) AS maxId FROM table WHERE 1 $randId=mt_rand((int)$row['minId'], (int)$row['maxId']); SELECT id,name,... FROM table WHERE id=$randId LIMIT 1 

为了从给定的表中select多个随机行(如“单词”),我们的团队提出了这个美:

 SELECT * FROM `words` AS r1 JOIN (SELECT MAX(`WordID`) as wid_c FROM `words`) as tmp1 WHERE r1.WordID >= (SELECT (RAND() * tmp1.wid_c) AS id) LIMIT n 

经典的“SELECT ID FROM TABLE ORDER BY RAND()LIMIT 1”实际上是可以的。

请参阅MySQL手册的摘录:

如果你在ORDER BY中使用了LIMIT row_count,MySQL一旦findsorting结果的第一个row_count行就结束sorting,而不是sorting整个结果。

订单哟将做一个完整的扫描表。 如果您执行select count(*),并且稍后在0和最后一个registry之间获得一个随机row = rownum,那么最好

看看Jan Kneschke的这个链接或者这个SO的答案,因为他们都讨论同一个问题。 所以答案也会覆盖各种选项,并根据您的需求提供一些很好的build议。 Jan将会遍历所有的各种选项和每个的性能特征。 他最终得到以下最优化的方法,通过它在MySQL select中做到这一点:

 SELECT name 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; 

HTH,

-蘸

我有点新的SQL,但如何在PHP中生成一个随机数和使用

 SELECT * FROM the_table WHERE primary_key >= $randNr 

这并不能解决表中的问题。

但是,这里有一个拉斯维加斯的build议:

 SELECT primary_key FROM the_table 

在PHP中使用mysql_num_rows()根据以上结果创build一个随机数:

 SELECT * FROM the_table WHERE primary_key = rand_number 

在旁边注意SELECT * FROM the_table有多慢:
创build一个基于mysql_num_rows()的随机数,然后将数据指针移动到该点mysql_data_seek() 。 只有一百万行的大桌子,这会有多慢?

我遇到了我的ID不连续的问题。 我想到了这个。

 SELECT * FROM products WHERE RAND()<=(5/(SELECT COUNT(*) FROM products)) LIMIT 1 

返回的行大约是5,但我把它限制为1。

如果你想添加另一个WHERE子句,它会变得更有趣一些。 假设你想在折扣上search产品。

 SELECT * FROM products WHERE RAND()<=(100/(SELECT COUNT(*) FROM pt_products)) AND discount<.2 LIMIT 1 

你必须做的是确保你返回的结果足够了,这就是为什么我将它设置为100.在子查询中有一个WHERE折扣<2子句是慢了10倍,所以最好返回更多的结果和限制。

我在这里看到很多解决scheme。 一两个似乎没问题,但其他解决scheme有一些限制。 但下面的解决scheme将适用于所有情况

 select a.* from random_data a, (select max(id)*rand() randid from random_data) b where a.id >= b.randid limit 1; 

在这里,id,不需要是顺序的。 它可以是任何主键/唯一/自动增量列。 请看下面最快的方法从一个大的MySQL表中select一个随机的行

感谢Zillur – http://www.techinfobest.com

使用下面的查询来获取随机行

 SELECT user_firstname , COUNT(DISTINCT usr_fk_id) cnt FROM userdetails GROUP BY usr_fk_id ORDER BY cnt ASC LIMIT 1 

在我的情况下,我的表有一个id作为主键,自动递增没有差距,所以我可以使用COUNT(*)MAX(id)获得行数。

我做了这个脚本来testing最快的操作:

 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 ms
  • 最大: 0.241041183472 ms
  • 订购0.216960906982 ms

用顺序方法回答:

 SELECT FLOOR(RAND() * ( SELECT id FROM tbl ORDER BY id DESC LIMIT 1 )) n FROM tbl LIMIT 1 ... SELECT * FROM tbl WHERE id = $result; 

我已经使用了这个工作做了从这里的参考

 SELECT * FROM myTable WHERE RAND()<(SELECT ((30/COUNT(*))*10) FROM myTable) ORDER BY RAND() LIMIT 30; 

创build一个函数来做到这一点最有可能的最好的答案和最快的答案在这里!

优点 – 即使有差距,速度也非常快。

 <? $sqlConnect = mysqli_connect('localhost','username','password','database'); function rando($data,$find,$max = '0'){ global $sqlConnect; // Set as mysqli connection variable, fetches variable outside of function set as GLOBAL if($data == 's1'){ $query = mysqli_query($sqlConnect, "SELECT * FROM `yourtable` ORDER BY `id` DESC LIMIT {$find},1"); $fetched_data = mysqli_fetch_assoc($query); if(mysqli_num_rows($fetched_data>0){ return $fetch_$data; }else{ rando('','',$max); // Start Over the results returned nothing } }else{ if($max != '0'){ $irand = rand(0,$max); rando('s1',$irand,$max); // Start rando with new random ID to fetch }else{ $query = mysqli_query($sqlConnect, "SELECT `id` FROM `yourtable` ORDER BY `id` DESC LIMIT 0,1"); $fetched_data = mysqli_fetch_assoc($query); $max = $fetched_data['id']; $irand = rand(1,$max); rando('s1',$irand,$max); // Runs rando against the random ID we have selected if data exist will return } } } $your_data = rando(); // Returns listing data for a random entry as a ASSOC ARRAY ?> 

请记住这个代码没有经过testing,但是是一个工作概念,即使有空白也能返回随机条目。只要差距不足以导致加载时间问题。

快速和肮脏的方法:

 SET @COUNTER=SELECT COUNT(*) FROM your_table; SELECT PrimaryKey FROM your_table LIMIT 1 OFFSET (RAND() * @COUNTER); 

第一个查询的复杂性是O(1)MyISAM表。

第二个查询伴随着一个表的全面扫描。 复杂性= O(n)

肮脏和快速的方法:

为此只保留一张单独的桌子。 只要插入到原始表中,您也应该将相同的行插入到此表中。 假设:没有DELETEs。

 CREATE TABLE Aux( MyPK INT AUTO_INCREMENT, PrimaryKey INT ); SET @MaxPK = (SELECT MAX(MyPK) FROM Aux); SET @RandPK = CAST(RANDOM() * @MaxPK, INT) SET @PrimaryKey = (SELECT PrimaryKey FROM Aux WHERE MyPK = @RandPK); 

如果DELETE被允许,

 SET @delta = CAST(@RandPK/10, INT); SET @PrimaryKey = (SELECT PrimaryKey FROM Aux WHERE MyPK BETWEEN @RandPK - @delta AND @RandPK + @delta LIMIT 1); 

总的复杂性是O(1)。

SELECT DISTINCT * FROM yourTable WHERE 4 = 4 LIMIT 1;