0

我已经在 php 页面上运行了 1000 次 foreach 循环。foreach 循环内的代码如下所示:

$first          = mysql_query("SELECT givenname FROM first_names order by rand() LIMIT 1");
$first_n        = mysql_fetch_array($first);
$first_name     = $first_n['givenname'];
$last           = mysql_query("SELECT surname FROM last_name order by rand() LIMIT 1");
$last_n         = mysql_fetch_array($last);
$last_name      = $last_n['surname'];
$first_lastname = $first_name . " " . $last_name;


$add     = mysql_query("SELECT streetaddress FROM user_addresss order by rand() LIMIT 1");  
$addr    = mysql_fetch_array($add);
$address = $addr['streetaddress'];

$unlisted  = "unlisted";
$available = "available";

$arr = array(
    $first_lastname,
    $address,
    $unlisted,
    $available
);

然后我一直在使用 array_rand 函数在每次循环运行时获取一个随机值:

<td><?php echo $arr[array_rand($arr)] ?></td>

所以加载 php 页面需要很长时间。有没有办法优化这段代码。因为每次循环运行时我都需要一个唯一值

4

3 回答 3

2

问题不在于您的 PHP foreach 循环。如果您通过 RAND() 对 MySQL 表进行排序,那么您就犯了一个严重的错误。让我向你解释一下当你这样做时会发生什么。

每次您发出 MySQL 请求时,MySQL 都会尝试将您的搜索参数(WHERE、ORDER BY)映射到索引以减少读取数据。然后它将相关信息加载到内存中进行处理。如果信息太大,它将默认将其写入磁盘并从磁盘读取以执行比较。您希望不惜一切代价避免磁盘读取,因为它们效率低下、缓慢、重复,并且有时在特定情况下可能完全错误。

当 MySQL 找到可以使用的索引时,它会改为加载索引表。索引表是内存位置和索引值之间的哈希表。因此,例如,主键的索引表如下所示:

  id      location
  1         0 bytes in
  2         17 bytes in
  3         34 bytes in

这是非常有效的,因为即使是非常大的索引表也可以放入少量的内存中。

为什么我要谈论指数?因为通过使用 RAND(),您正在阻止 MySQL 使用它们ORDER BY RAND()强制 MySQL 为每一行创建一个新的随机值。这需要 MySQL 将所有表数据复制到所谓的临时表中,并添加一个带有 RAND() 值的新字段。该表将太大而无法存储在内存中,因此将存储到磁盘。

当您告诉 MySQL ORDER BY RAND() 并创建表时,MySQL 将成对比较每一行(MySQL 排序使用快速排序)。由于行太大,您正在查看此操作的相当多的磁盘读取。完成后,它会返回,并且您会以巨大的成本获得数据。

有很多方法可以防止这种巨大的开销 SNAFU。其中之一是从 RAND() 中选择 ID 为最大索引并限制为 1。这不需要创建额外的字段。有很多类似的 Stack 问题。

于 2013-05-14T17:12:51.663 回答
0

已经解释了为什么应该避免 ORDER BY RAND(),所以我只是提供了一种方法来通过一些更快的查询来做到这一点。

首先根据您的表格大小获取一个随机数:

SELECT FLOOR(RAND()*COUNT(*)) FROM first_names

第二次在限制中使用随机数

SELECT * FROM first_names $pos,1

不幸的是,我认为没有任何方法可以将这两个查询合二为一。

你也可以做 a SELECT COUNT(*) FROM first_names,存储数字,并在 PHP 中生成随机 $pos 任意次数。

于 2013-05-14T17:21:20.627 回答
0

如果您的主机支持,您应该切换到使用 mysqli 或 pdo,但这样的东西应该可以工作。如果您在任何一个表中都没有足够的记录,您将必须确定要做什么(array_pad 或包装索引并重新启动)

function getRandomNames($qty){
    $qty = (int)$qty;
    $fnames = array();
    $lnames = array();
    $address = array();

    $sel =mysql_query("SELECT givenname FROM first_names order by rand() LIMIT ".$qty);
    while ($rec = mysql_fetch_array($sel)){$fnames[] = $rec[0]; }

    $sel =mysql_query("SELECT surname FROM last_name order by rand() LIMIT ".$qty);
    while ($rec = mysql_fetch_array($sel)){ $lnames[] = $rec[0]; }

    $sel =mysql_query("SELECT streetaddress FROM user_addresss order by rand() LIMIT ".$qty);
    while ($rec = mysql_fetch_array($sel)){ $address[] = $rec[0]; }

    // lets stitch the results together
    $results = array();
    for($x = 0; $x < $qty; $x++){
       $results[] = array("given_name"=>$fnames[$x], "surname"=>$lnames[$x], "streetaddress"=>$address[$x]);
    }
    return $results;
}

希望这可以帮助

更新

根据 Sébastien Renauld 的回答,一个更完整的解决方案可能是将查询结构更像

"SELECT givenname from first_names where id in (select id from first_names order by rand() limit ".$qty.")";
于 2013-05-14T17:23:55.827 回答