从MySQL中选择可变数量的随机记录

时间:2009-02-17 21:06:47

标签: php mysql random

我想从数据库中显示一条随机记录。如果我选择,我希望能够显示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()有效,但我可以做得更好吗?

MORE UPDATE ID中有漏洞。没有大量的流失,但任何流失都需要得到我们团队的批准,因此可以处理以转储任何缓存。

感谢您的回复!

8 个答案:

答案 0 :(得分:3)

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

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

Select *
from site_info
Order by Rand()
LIMIT N

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

修改
您是否已将您的代码与查询解决方案进行了分析?我想你只是在这里进行预优化。

答案 1 :(得分:3)

如果您不想通过rand()选择订单。

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

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

编辑:使用key =&gt;返回键数组而不是新数组值

答案 2 :(得分:1)

好吧,我不认为ORDER BY RAND()在一个只有66行的表中会那么慢,但是你可以考虑一些不同的解决方案。

数据是否真的稀疏和/或经常更新(因此ID中存在很大差距)?

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

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

答案 3 :(得分:1)

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

我的回答

/***************************************************/
/* 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函数返回一个set或single结果,则它具有不同的输出。我很懒,并没有完全实现条件来处理这种情况。

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

答案 4 :(得分:0)

mysql_query("SELECT id FROM site_info WHERE major <> 0 ORDER BY RAND() LIMIT $intNumberofSites")

EDIT 该死的,JPunyon有点快:)

答案 5 :(得分:0)

试试这个:

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没有第二次机会。

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

答案 6 :(得分:-1)

我只想使用rand()函数(我假设您使用的是MySQL)......

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

答案 7 :(得分:-1)

我和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;
}