【问题标题】:Select variable number of random records from MySQL从 MySQL 中选择可变数量的随机记录
【发布时间】:2010-10-08 04:57:55
【问题描述】:

我想显示数据库中的随机记录。如果我选择,我希望能够显示 X 条随机记录。因此,我需要从随机选择的 ID 列表中选择前 X 条记录

(永远不会有超过 500 条记录可供选择,除非地球的大小急剧增加。目前有 66 种可能。)

这个功能有效,但我怎样才能使它更好呢?

/***************************************************/
/* RandomSite */
//****************/
//  Returns an array of random site IDs or NULL
/***************************************************/   
function RandomSite($intNumberofSites = 1) {
    $arrOutput = NULL;
    //open the database
    GetDatabaseConnection('dev');

    //inefficient
    //$strSQL = "SELECT id FROM site_info WHERE major <> 0 ORDER BY RAND() LIMIT ".$intNumberofSites.";";

    //Not wonderfully random
    //$strSQL = "SELECT id FROM site_info WHERE major <> 0 AND id >= (SELECT FLOOR( COUNT(*) * RAND()) FROM site_info ) ORDER BY id LIMIT ".$intNumberofSites.";";

    //Manual selection from available pool of candidates  ?? Can I do this better ??
    $strSQL = "SELECT id FROM site_info WHERE major <> 0;";

    if (is_numeric($intNumberofSites))
    {
        //excute my query
        $result = @mysql_query($strSQL);
        $i=-1;

        //create an array I can work with  ?? Can I do this better ??
        while ($row = mysql_fetch_array($result, MYSQL_NUM))
        {
            $arrResult[$i++] = $row[0];
        }

        //mix them up
        shuffle($arrResult);

        //take the first X number of results  ?? Can I do this better ??
        for ($i=0;$i<$intNumberofSites;$i++)
        {
            $arrOutput[$i] = $arrResult[$i];
        }
    }   

    return $arrOutput;
    }

更新问题: 我知道 ORDER BY RAND(),我只是不想使用它,因为有传言说它在扩展和性能方面并不是最好的。我对我的代码过于挑剔。我有什么作品,ORDER BY RAND() 作品,但我可以做得更好吗?

更多更新 身份证上有洞。没有大量的流失,但发生的任何流失都需要得到我们团队的批准,因此可以处理转储任何缓存。

感谢您的回复!

【问题讨论】:

  • 如果最多只能有 500 个,那么 Rand() 的 Order 非常快。
  • 同意 JPunyon 关于预优化的观点,并且鉴于最多有 500 条记录,不同的解决方案会快多少,当前的功能会那么慢吗?

标签: php mysql random


【解决方案1】:

为什么不在数据库查询的 orderby 中使用 Rand 函数?那么你就不必在代码中进行随机化等...

类似的东西(我不知道这是否合法)

Select *
from site_info
Order by Rand()
LIMIT N

其中 N 是您想要的记录数...

编辑
您是否分析了您的代码与查询解决方案?我认为您只是在这里进行了预优化。

【讨论】:

  • 它完全有效,但网络上的说法是它不能很好地扩展。为了成为最好的我,我正在被超优化=P
  • 如果世界规模急剧扩大,您只有 500 条记录...您想获得多高的效率?
  • 我完全在预优化。这更像是代码高尔夫而不是解决方案交付
  • 你得到了答案,因为你的解决方案不是最快的(在我的测试中是 8/100 秒),但是看起来最好的并且性能很好
【解决方案2】:
mysql_query("SELECT id FROM site_info WHERE major <> 0 ORDER BY RAND() LIMIT $intNumberofSites")

编辑 该死的,JPunyon 快了一点:)

【讨论】:

    【解决方案3】:

    我会简单地使用 rand() 函数(我假设您使用的是 MySQL)...

    SELECT id, rand() as rand_idx FROM site_info WHERE major <> 0 ORDER BY rand_idx LIMIT x;
    

    【讨论】:

      【解决方案4】:

      如果你不想按 rand() 的顺序选择。

      在结果上使用array_rand,而不是改组:

      $randKeys = array_rand($arrResult, $intNumberofSites);
      $arrOutput = array_intersect_key(array_flip($randKeys), $arrResult);
      

      编辑:返回键数组而不是键 => 值的新数组

      【讨论】:

      • 但是当您只需要其中的一部分时,为什么要选择所有行并将它们全部推送到数组中。这是对资源和内存的浪费。
      • 我选择了 rand()。但是,如果他想将它们全部拉到一个数组中,或者如果它们是相当静态的并且他想缓存它们,那么这是另一种方式,然后是 shuffle,for。结果并没有我最初想的那么好……:P
      • 我想给你点,但你的解决方案(如你所说)平均来说是最慢的超过 10,000 次迭代。我没有为测试实现任何缓存,所以你可能在那里赢了。不过答案很好,我学到了重点
      【解决方案5】:

      好吧,我认为 ORDER BY RAND() 在只有 66 行的表中不会那么慢,但无论如何您都可以研究一些不同的解决方案。

      数据真的很稀疏和/或经常更新吗(所以 id 有很大的差距)?

      假设它不是很稀疏,您可以从表中选择最大 id,使用 PHP 的内置随机函数在 1 和最大 id 之间选择 N 个不同的数字,然后尝试从表中获取具有这些 id 的行桌子。如果返回的行数少于选择的数字,请获取更多的随机数并重试,直到获得所需的行数。这也可能不是特别快。

      如果数据稀疏,我会设置一个辅助“id-type”列,确保它是连续的。因此,如果表中有 66 行,请确保新列包含值 1-66。每当在表中添加或删除行时,您都必须做一些工作来调整此列中的值。然后使用与上面相同的技术,在 PHP 中选择随机 ID,但您不必担心“丢失 ID?重试”的情况。

      【讨论】:

        【解决方案6】:

        试试这个:

        SELECT
          @nv := @min + (RAND() * (@max - @min)) / @lc,
          (
          SELECT
            id
          FROM  site_info
          FORCE INDEX (primary)
          WHERE id > @nv
          ORDER BY
            id
          LIMIT 1
          ),
          @max,
          @min := @nv,
          @lc := @lc - 1
        FROM
          (
          SELECT @min := MIN(id)
          FROM site_info
          ) rmin,
          (
          SELECT @max := MAX(id)
          FROM site_info
          ) rmax,
          (
          SELECT @lc := 5
          ) l,
          site_info
        LIMIT 5
        

        这将使用索引在每次迭代中按降序选择一个随机 ID。

        不过,您获得的结果可能会比您想要的少,因为它不会给错过的 id 提供第二次机会。

        您选择的行数越多,机会就越大。

        【讨论】:

          【解决方案7】:

          我和JPunyon 在一起。使用ORDER BY RAND() LIMIT $N。我认为$arrResult 拥有和改组这么多(未使用的)条目比使用 MySQL RAND() 函数对性能的影响更大。

          function getSites ( $numSites = 5 ) {
          
              // Sanitize $numSites if necessary
          
              $result = mysql_query("SELECT id FROM site_info WHERE major <> 0 "
                                   ."ORDER BY RAND() LIMIT $numSites");
          
              $arrResult = array();
          
              while ( $row = mysql_fetch_array($result,MYSQL_NUM) ) {
                  $arrResult[] = $row;
              }
          
              return $arrResult;
          }
          

          【讨论】:

          • 我敢打赌,网络(MySQL 服务器到 PHP 服务器)在传输不需要的项目方面的贡献比拥有和改组它们的数量级或更多。
          • 我相信您是对的,但无论哪种方式,您都希望尽可能少地在数据库和 Web 服务器之间进行传输。
          【解决方案8】:

          这是我编写和测试的三个函数

          我的回答

          /***************************************************/
          /* RandomSite1 */
          //****************/
          //  Returns an array of random rec site IDs or NULL
          /***************************************************/   
          function RandomSite1($intNumberofSites = 1) {
              $arrOutput = NULL;
              GetDatabaseConnection('dev');
              $strSQL = "SELECT id FROM site_info WHERE major <> 0;";
              if (is_numeric($intNumberofSites))
              {
                  $result = @mysql_query($strSQL);
                  $i=-1;
                  while ($row = mysql_fetch_array($result, MYSQL_NUM)) {
                      $arrResult[$i++] = $row[0]; }
                  //mix them up
                  shuffle($arrResult);
                  for ($i=0;$i<$intNumberofSites;$i++) {
                      $arrOutput[$i] = $arrResult[$i]; }
              }   
              return $arrOutput;
              }
          

          JPunyon 和许多其他人

          /***************************************************/
          /* RandomSite2 */
          //****************/
          //  Returns an array of random rec site IDs or NULL
          /***************************************************/   
          function RandomSite2($intNumberofSites = 1) {
              $arrOutput = NULL;
              GetDatabaseConnection('dev');
              $strSQL = "SELECT id FROM site_info WHERE major<>0 ORDER BY RAND() LIMIT ".$intNumberofSites.";";
              if (is_numeric($intNumberofSites))
              {
                  $result = @mysql_query($strSQL);
                  $i=0;
                  while ($row = mysql_fetch_array($result, MYSQL_NUM)) {
                      $arrOutput[$i++] = $row[0]; }
              }   
              return $arrOutput;
              }
          

          具有满足我问题意图的创造性解决方案的 OIS。

          /***************************************************/
          /* RandomSite3 */
          //****************/
          //  Returns an array of random rec site IDs or NULL
          /***************************************************/   
          function RandomSite3($intNumberofSites = 1) {
              $arrOutput = NULL;
              GetDatabaseConnection('dev');
              $strSQL = "SELECT id FROM site_info WHERE major<>0;";
              if (is_numeric($intNumberofSites))
              {
                  $result = @mysql_query($strSQL);
                  $i=-1;
                  while ($row = mysql_fetch_array($result, MYSQL_NUM)) {
                      $arrResult[$i++] = $row[0]; }
                  $randKeys = array_rand($arrResult, $intNumberofSites);
                  $arrOutput = array_intersect_key($randKeys, $arrResult);
              }   
              return $arrOutput;
              }
          

          我做了一个简单的 10,000 次迭代循环,其中我提取了 2 个随机站点。我为每个功能关闭并打开了一个新浏览器,并在运行之间清除了缓存。我运行了 3 次测试以获得一个简单的平均值。

          注意 - 第三种解决方案在提取少于 2 个站点时失败,因为如果 array_rand 函数返回一个集合或单个结果,则它具有不同的输出。我很懒,没有完全实现条件来处理这种情况。

          • 1 平均:12.38003755 秒
          • 2 平均:12.47702177 秒
          • 3 平均:12.7124153 秒

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2019-03-25
            • 1970-01-01
            • 2018-11-19
            • 2011-09-05
            • 2012-12-24
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多