2013-05-14 77 views
0

我一直在php页面上运行一个foreach循环1000次。 foreach循环内的代码看起来象下面这样:在php中优化for循环

$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页面正在采取真的很长时间。有没有一种方法可以优化此代码。由于每次循环运行时我需要一个唯一的值

+0

为什么哟你甚至使用循环?你从SQL的'rand()'中获得一个随机值? – Aquillo 2013-05-14 17:08:25

+0

您必须一次从数据库中选择所有记录。每个查询都很昂贵。 – 2013-05-14 17:08:26

+2

这不是导致这个“真正长时间”的循环,而是您查询的数量:准确的是2000。 – 2013-05-14 17:08:58

回答

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问题。

+0

+1很好的解释 – 2013-05-14 17:19:41

+0

@Orangepill:还有其他方法可以做到这一点。不幸的是,我使用的设备不支持复制粘贴,因此粘贴链接将会很困难。在Stack上搜索SELECT BY RAND(),你应该得到一些命中。 – 2013-05-14 17:30:24

+0

@SébastienRenauld最好的答案可能是你和我的混搭 – Orangepill 2013-05-14 17:34:56

0

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

首先能根据您的表大小的随机数:

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

第二次使用随机数的限制

SELECT * FROM first_names $pos,1 

不幸的是,我不认为有任何方式的结合两个查询合而为一。

你也可以做一个SELECT COUNT(*) FROM first_names,存储号码,并随机生成PHP $多次,只要你喜欢。

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

希望这有助于

UPDATE

基于塞巴斯蒂安Renauld的回答更完整的解决方案可能是结构化查询更像

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