在php中优化循环

时间:2013-05-14 17:05:03

标签: php for-loop

我在php页面上运行了一次foreach循环1000次。 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页面需要很长时间。有没有办法可以优化这段代码。因为每次循环运行时我都需要一个唯一的值

3 个答案:

答案 0 :(得分: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排序使用quicksort)。由于行太大,您正在查看此操作的相当多的磁盘读取。完成后,它会返回,并且您获得的数据非常耗费。

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

答案 1 :(得分:0)

已经解释了为什么要避免使用ORDER BY RAND(),所以我只是提供了一种方法,可以通过一些更快的查询来实现。

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

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

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

SELECT * FROM first_names $pos,1

不幸的是,我认为没有办法将两个查询合并为一个。

此外,您可以执行SELECT COUNT(*) FROM first_names,存储号码,并在PHP中随机生成$ pos随机$ pos。

答案 2 :(得分: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ébastienRenauld的回答,一个更完整的解决方案可能是构建查询更像

"SELECT givenname from first_names where id in (select id from first_names order by rand() limit ".$qty.")";