2013-05-14 152 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.")";