48

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

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

4

24 回答 24

51

获取所有 id,从中随机选择一个,然后检索整行。

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

如果这里和那里有洞,但主要是顺序值,并且您不关心稍微偏斜的随机性,请获取最大值,计算 id,然后选择 id 等于或高于您计算的第一行。倾斜的原因是 id 跟随这样的洞将比跟随另一个 id 的洞更有可能被选中。

如果您随机订购,您将面临可怕的表格扫描,并且“快速”一词不适用于此类解决方案。

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

于 2008-10-17T07:42:27.313 回答
39

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

一种无需外部代码参与的快速方法,赞

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;
于 2008-10-17T08:19:30.890 回答
30

MediaWiki 使用了一个有趣的技巧(用于 Wikipedia 的 Special:Random 功能):包含文章的表有一个带有随机数的额外列(在创建文章时生成)。要获得一篇随机文章,请生成一个随机数,并在随机数列中获取具有下一个更大或更小(不记得是哪个)值的文章。使用索引,这可以非常快。(而且 MediaWiki 是用 PHP 编写并为 MySQL 开发的。)

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

于 2008-10-18T04:39:21.317 回答
13

这是一个运行相当快的解决方案,它获得了更好的随机分布,而不依赖于连续的 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;
于 2008-10-17T18:16:48.873 回答
4

也许您可以执行以下操作:

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

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

于 2008-09-26T22:15:52.187 回答
3

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

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

SELECT * FROM `foo` WHERE `foo_rand` >= {some random value} LIMIT 1
于 2008-09-26T22:17:12.217 回答
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
于 2009-04-23T09:10:27.933 回答
1

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

于 2009-11-30T21:01:18.110 回答
1

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

(如果你知道最小 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
于 2010-05-31T20:53:12.610 回答
1

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

于 2011-01-25T13:50:39.307 回答
1

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

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

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

谢谢 Zillur - www.techinfobest.com

于 2014-02-22T09:22:38.323 回答
0

经典的“SELECT id FROM table ORDER BY RAND() LIMIT 1”其实没问题。

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

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

于 2008-09-27T13:12:13.937 回答
0

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

SELECT * from TABLE order by RAND() LIMIT 1
于 2008-10-17T07:39:09.333 回答
0

用一个命令你会做一个完整的扫描表。最好是执行 select count(*) 并稍后在 0 和最后一个注册表之间获得一个随机 row=rownum

于 2008-10-17T07:44:40.040 回答
0

在伪代码中:

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

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

于 2008-10-17T07:53:46.277 回答
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()。在有一百万行的大表上,这会有多慢?

于 2008-12-19T22:27:28.230 回答
0

看看Jan Kneschke的这个链接或这个 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;

高温下,

-蘸

于 2009-03-20T22:07:50.487 回答
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 discount<.2 子句会慢 10 倍,因此最好返回更多结果和限制。

于 2012-05-30T22:37:58.937 回答
0

使用以下查询获取随机行

SELECT user_firstname ,
COUNT(DISTINCT usr_fk_id) cnt
FROM userdetails 
GROUP BY usr_fk_id 
ORDER BY cnt ASC  
LIMIT 1
于 2015-02-24T06:17:56.113 回答
0

在我的情况下,我的表有一个 id 作为主键,自动递增,没有间隙,所以我可以使用COUNT(*)orMAX(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
  • 最大限度: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;
于 2015-05-15T14:27:49.570 回答
0

我已经使用了这个,并且工作已经完成了这里的参考

SELECT * FROM myTable WHERE RAND()<(SELECT ((30/COUNT(*))*10) FROM myTable) ORDER BY RAND() LIMIT 30;
于 2016-03-12T16:36:30.473 回答
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
?>

请记住,此代码未经测试,但它是一个工作概念,即使有间隙也可以返回随机条目。只要间隙不足以导致加载时间问题。

于 2017-03-29T18:58:45.243 回答
-1

快速而肮脏的方法:

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

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

对于 MyISAM 表,第一个查询的复杂度是 O(1)。

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

又脏又快的方法:

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

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

如果允许删除,

SET @delta = CAST(@RandPK/10, INT);

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

总体复杂度为 O(1)。

于 2008-10-18T05:18:07.120 回答
-2

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

于 2014-01-22T02:53:57.493 回答