クエリを毎回投げるのと、まとめて取得したデータをループで整形するときの違い

2016/03/23

いわゆる親子関係のデータの一覧を取得する時にいつも悩むのが毎回クエリを投げるべきか、まとめて取得してループで整形するのか・・・ってこと。

つまり・・・
「親一覧」から任意の「親詳細」を表示して紐づく「子一覧」を表示』ではなくて、
「各親」に「子一覧」を含ませた「親一覧」を表示』ってヤツをしたい

メモ

やりたいこと:カテゴリ名と紐づく詳細情報をまとめて表示したい。
※ブログでいうトコのカテゴリと紐づく記事をまとめて表示。

▼テーブルは基本、下記のような構成になると思われる

  • カテゴリデータを格納するテーブル
  • 詳細情報を格納するテーブル
  • カテゴリと詳細情報を紐付けるテーブル(状況によっては不要)

▼処理手順A(問題点:クエリをいっぱい投げる)

  1. 親一覧を取得
  2. 親一覧をループさせて、子一覧を合体

▼処理手順B(問題点:配列だからメモリが気になる)

  1. 親一覧を取得
  2. 子一覧を取得
  3. 親と子をループで合体。

レコードが多いときに「処理手順B」にするとメモリ不足で落ちるだろうから、親・子の件数に併せて使い分けるってのが妥当ってのは分かるんだけど・・・「レコード数が1000件程度ならどうなの?」って感じ。

▼処理手順Aのサンプル

$parentsData = 親のレコード取得するクエリを投げる

$result = array();
foreach ( $parentsData as $rowParent ) {
  $result[] = array(
    “parent” => $rowParent,
    “children” => 紐づく子のレコードを取得するクエリを投げる,
  );
}

▼処理手順Bのサンプル

$relationData = 親子の紐付けを取得するクエリを投げる
$parentsData = 親のレコード取得するクエリを投げる
$childrenData = 子のレコード取得するクエリを投げる

$result = array();
foreach ($relationData as $rowRelation) {
  $pId = $rowRelation[親ID];
  $cId = $rowRelation[子ID];
  
  foreach ($parentsData as $rowParent) {
    if ( $pId == $rowParent[ID] ) {
      if ( !isset($result[$pId]) ) {
        $result[$pId] = array(
         “parent” => $rowParent,
         “children” => array(),
        );
      }
      
      break;
    }
  }
  
  if ( !isset($result[$pId]) ) {
    echo “親データが見つからなかった?”;
    exit;
  }
  
  foreach ($childrenData as $rowChild) {
    if ( $cId == $rowChild[ID] ) {
      $result[$pId][“children”][] = $rowChild;
      break;
    }
  }
}

圧倒的に「処理手順A」のソースのほうがシンプルで後々メンテしやすいってのは分かる・・・。

結果

レコードを新たに用意するのが面倒だったので・・・すでに持っているデータで試してみる。
※今回は「子が持つ親の数は複数」ではなく「子が持つ親の数は1つ」でやってみるので、「処理手順B」のソースはもっとシンプルになってくる。

▼レコード数

  • 親・レコード数:6683件
  • 子・レコード数:3552件

▼処理手順Aの概要

親レコード取得
foreach (親レコード){
  各親レコードのIDから紐づく子レコード取得
}

▼処理手順Bの概要

親レコード取得
子レコード取得
foreach (子レコード){
  foreach (親レコード){
    各親に紐づく子レコードを格納
  }
}

▼処理手順Aの結果(親レコードを全件、1000件、250件取得で試した結果)

■クエリを投げる回数:6684回(親の取得1回+親に紐づく子の取得6683回)
MEMORY : 16,486,024 byte
44.2154秒

■クエリを投げる回数:1001回(親の取得1回+親に紐づく子の取得1000回)
MEMORY : 4,259,152 byte
7.5729秒

■クエリを投げる回数:251回(親の取得1回+親に紐づく子の取得250回)
MEMORY : 2,654,960 byte
2.6557秒

▼処理手順Bの結果

■クエリを投げる回数:2回(親の取得1回+子の取得1回であとはループで整形)
MEMORY : 19,674,464 byte
11.6264秒

メモリで落ちにくい「処理手順A」、メモリをくうけど処理時間が短い「処理手順B」ってのがよくわかった。
※結果は、目安程度で。ひょっとしたらソースに重大な欠陥があってのこの結果かもしれない。

結論としては、レコード数が多いなら「処理手順A」でやるか、そもそも見せ方を考え直せって話しなんだよね。
レコード数が少ないときに限って「処理手順B」でやってOKって感じ。
ただ具体的に何レコードが多い・少ないってのは分からないけど、500レコードぐらいまでなら「処理手順B」でOKじゃないかなぁ?
1000レコードあたりになると、そもそも「見づらい」ってのがあるから見せ方を変えるべきだよね。
※500レコードでも「見づらい」んだけどさ。

新着(ニュース関連以外)

2017-12-13
jqueryで取得したDOM要素をオブジェクトじゃなくて、配列で受け取りたい
2017-11-30
Xampp+FuelPHP1.8をサーバーにupしたらエラーになった。
2017-11-09
PCでプッシュ通知ってのをしたい。
2017-11-06
PHPのバージョンを上げたらwikiが壊れたっぽい。
2017-08-03
formのinputでmaxlengthを使うとFirefoxでバグってた。他のブラウザでも気づかずにバグってたかもしれない。