0

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 ページの読み込みには非常に時間がかかります。このコードを最適化する方法はありますか? ループが実行されるたびに一意の値が必要になるため

4

3 に答える 3

2

問題は PHP の foreach ループではありません。MySQL テーブルを RAND() で並べ替えると、重大な間違いを犯しています。これを行うとどうなるか説明しましょう。

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 を防ぐ方法はたくさんあります。それらの 1 つは、RAND() から最大インデックスまでの ID を選択し、1 で制限することです。これには、追加のフィールドを作成する必要はありません。同様のスタックの質問がたくさんあります。

于 2013-05-14T17:12:51.663 に答える
0

ORDER BY RAND() を避けるべき理由は既に説明したので、より高速なクエリでそれを行う方法を提供するだけです。

まず、テーブル サイズに基づいて乱数を取得します。

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

2番目に制限内で乱数を使用する

SELECT * FROM first_names $pos,1

残念ながら、2 つのクエリを 1 つに結合する方法はないと思います。

またSELECT COUNT(*) FROM first_names、PHP で何度でも a を実行し、数値を保存し、ランダムな $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's answer に基づいて、より完全な解決策は、クエリを次のように構造化することです。

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