从mysql中的大表中快速选择随机行

时间:2008-10-17 07:38:25

标签: mysql sql select random

从大型mysql表中选择随机行的快速方法是什么?

我正在使用php,但我对任何解决方案感兴趣,即使它是用另一种语言。

24 个答案:

答案 0 :(得分:46)

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

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

如果这里和那里有漏洞,但主要是顺序值,并且你不关心稍微偏斜的随机性,抓住最大值,计算一个id,然后选择id等于或高于一个id的第一行你算了倾斜的原因是id跟随这样的洞将被选中的机会高于跟随另​​一个id的那些洞。

如果您是随机订购的,那么您的手上会有一个糟糕的桌面扫描,而 quick 这个词并不适用于这样的解决方案。

不要这样做,也不应该通过GUID订购,它也有同样的问题。

答案 1 :(得分:37)

我知道必须有一种方法可以在一个查询中快速完成。这是:

没有外部代码参与的快速方式,赞赏

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;

答案 2 :(得分:30)

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

如果结果数字分布不均,这种方法可能会导致问题; IIRC,这已在MediaWiki上修复,所以如果您决定这样做,您应该查看代码以了解它当前是如何完成的(可能它们会定期重新生成随机数列)。

答案 3 :(得分:12)

这是一个运行相当快的解决方案,它可以获得更好的随机分布,而不依赖于连续的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;

答案 4 :(得分:3)

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

更新:在检索时,您仍需要在发出SELECT语句之前计算一些随机值,例如,

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

答案 5 :(得分:3)

也许你可以这样做:

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

这假设你的身份证号码都是连续的,没有间隙。

答案 6 :(得分:1)

在伪代码中:

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

这假定id是唯一(主要)密钥。

答案 7 :(得分:1)

一种简单但缓慢的方式(适用于小桌子)

SELECT * from TABLE order by RAND() LIMIT 1

答案 8 :(得分:1)

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

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

答案 9 :(得分:1)

还有另一种方法可以仅使用查询生成随机行,而不使用rand()进行排序。 它涉及用户定义的变量。 见how to produce random rows from a table

答案 10 :(得分:1)

如果您不删除此表中的行,最有效的方法是:

(如果你知道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

答案 11 :(得分:1)

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

答案 12 :(得分:0)

我用过这个,工作完成了 来自here

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

答案 13 :(得分:0)

创建一个函数来实现这个最有可能是最好的答案和最快的答案!

优点 - 即使有差距也非常快。

<?

$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
?>

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

答案 14 :(得分:0)

在我的情况下,我的表有一个id为主键,自动增加,没有间隙,所以我可以使用COUNT(*)MAX(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 ms
  • Max: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;

答案 15 :(得分:0)

使用以下查询获取随机行

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

答案 16 :(得分:0)

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

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

这里,id,不需要是顺序的。它可以是任何主键/唯一/自动增量列。请参阅以下Fastest way to select a random row from a big MySQL table

由于 Zillur - www.techinfobest.com

答案 17 :(得分:0)

我遇到了我的ID不顺序的问题。我想出了什么。

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

返回的行大约为5,但我将其限制为1.

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

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

你需要做的是确保你返回足够的结果,这就是为什么我把它设置为100.子查询中的WHERE折扣&lt; .2子句慢了10倍,所以最好返回更多的结果和限制

答案 18 :(得分:0)

请看Jan Kneschke的this linkthis SO answer,因为他们都讨论同一个问题。 SO答案也适用于各种选项,并根据您的需求提供一些很好的建议。 Jan介绍了各种各样的选项和性能特征。他最终获得​​了以下最佳方法,以便在MySQL选择中执行此操作:

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,

-Dipin

答案 19 :(得分:0)

我对SQL有点新手但是如何在PHP中生成随机数并使用

SELECT * FROM the_table WHERE primary_key >= $randNr

这并没有解决表中漏洞的问题。

但这是对lassevks建议的一个转折:

SELECT primary_key FROM the_table

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

SELECT * FROM the_table WHERE primary_key = rand_number

另一方面注意SELECT * FROM the_table有多慢:
基于mysql_num_rows()创建随机数,然后将数据指针移动到该点mysql_data_seek()。这个数百万行的大型表会有多慢?

答案 20 :(得分:0)

订单哟会做一个完整的扫描表。 如果你做一个选择计数(*),然后在0和最后一个注册表之间得到一个随机row = rownum,那就最好了

答案 21 :(得分:0)

经典的“SELECT id FROM table ORDER BY RAND()LIMIT 1”实际上是正常的。

请参阅MySQL手册的以下摘录:

如果将LIMIT row_count与ORDER BY一起使用,MySQL会在找到排序结果的第一行row_count行后立即结束排序,而不是对整个结果进行排序。

答案 22 :(得分:-1)

快速而肮脏的方法:

SET @COUNTER=SELECT COUNT(*) FROM your_table;

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

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

第二个查询伴随表格全扫描。复杂度= O(n)

肮脏而快速的方法:

仅为此目的保留一个单独的表格。每当插入原始表时,您还应该向该表插入相同的行。假设:没有DELETE。

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)。

答案 23 :(得分:-2)

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