Php MySQL-从大表中选择随机行

Php MySQL-从大表中选择随机行,php,mysql,random,Php,Mysql,Random,我很抱歉,如果这个主题已经做到死,但我正在努力从一个大的MySQL表中选择一个随机行。这是一个名为photos的表,其主键是PhotoID。目前,它的ID范围从~1500(由于在测试中创建了行,然后删除了行)到~12000(有一些间隙),我预计它会变得更大 虽然它相对较小,但我一直在使用: SELECT PhotoID FROM photos …放入PHP数组$All_id中,然后在PHP中: $RandomID = $All_IDs[mt_rand(0,count($All_IDs)-1

我很抱歉,如果这个主题已经做到死,但我正在努力从一个大的MySQL表中选择一个随机行。这是一个名为photos的表,其主键是PhotoID。目前,它的ID范围从~1500(由于在测试中创建了行,然后删除了行)到~12000(有一些间隙),我预计它会变得更大

虽然它相对较小,但我一直在使用:

 SELECT PhotoID FROM photos
…放入PHP数组$All_id中,然后在PHP中:

 $RandomID = $All_IDs[mt_rand(0,count($All_IDs)-1)]
然后:

这很好用,当我重复它的时候,我会得到一系列很好的随机照片。然而,我不认为加载整个PhotoID列来选择一个随机ID,然后再进行另一个查询来获取该记录是非常有效的,特别是如果我要选择几个。同样,我不希望选择整个表,将所有列都放入一个数组中,而只是选择一个。在其他几个StackOverflow答案的帮助下,我得出了以下结论:

SELECT MIN(PhotoID) INTO @MinID FROM photos;
SELECT MAX(PhotoID) INTO @MaxID FROM photos;
SELECT PhotoID,/* other columns */ FROM photos WHERE PhotoID >= (@MinID + RAND() * (@MaxID - @MinID)) ORDER BY PhotoID LIMIT 0,1
我原以为这会管用,但我发现重复这个查询几次只会给我一个很短的ID分布,在1500-1700范围内,而如上所述,ID目前接近12000。我不明白这是为什么?

请尝试以下查询:

select * from photos order by rand() limit 1;

我怀疑您看到的值范围很小,因为WHERE子句中的RAND是针对表中的每一行计算的。行上的PhotoID更有可能大于右侧表达式返回的较低值。因此,查询返回的集合对较低的PhotoID值的权重更大。有了订单,你会得到最低的

要获得更随机的分布,只需对RAND进行一次评估。另外,如果我可以在一条语句中完成工作,并且没有用户定义的变量,那么我不希望在三条单独的SELECT语句中执行多个查询

要实现您试图实现的算法,我将采用以下方法:

  SELECT t.photoid 
       , ...
    FROM photos t
    JOIN ( SELECT m.min_id + RAND() * (max_id - min_id) AS _rand
             FROM ( SELECT MIN(p.photoid) AS min_id
                         , MAX(p.photoid) AS max_id
                      FROM photos p
                   ) m
         ) r
      ON r._rand <= t.photoid
   ORDER BY t.photoid
   LIMIT 1

在MySQL中,MySQL术语中的内联视图派生表将首先具体化,然后是外部查询。由于m返回一行,因此r中的RAND函数将只计算一次。然后表达式中的单个值将用于外部查询。

请注意,MySQL将为表中的每一行计算RAND函数。然后,结果集将需要一个usingfilesort操作来标识RAND值最低的行。这种方法往往不能很好地扩展到大型集合。这很理想,谢谢。。。我避免按RAND排序,因为我知道每一行都会调用RAND,但我认为只有当它是WHERE子句的一部分时才会调用一次。注意:这种方法不一定是从集合中返回随机行的最佳方法。我试图解释原始查询中观察到的行为的原因,以及实现原始查询的预期设计的示例。在本例中使用JOIN是有效的,因为内联视图r将返回一行。如果出于某种原因需要使用多个语句(如原始语句),请将RAND操作移出到单独的语句中,并将单个静态值传递到实际查询中。这就是这个答案中的查询所做的。
  SELECT t.photoid 
       , ...
    FROM photos t
    JOIN ( SELECT m.min_id + RAND() * (max_id - min_id) AS _rand
             FROM ( SELECT MIN(p.photoid) AS min_id
                         , MAX(p.photoid) AS max_id
                      FROM photos p
                   ) m
         ) r
      ON r._rand <= t.photoid
   ORDER BY t.photoid
   LIMIT 1