forループから集合演算へ

たまに SQL で取得した ID を使って foreach ループを組んで更に新しいカラムを取得するプログラムを見かけますが、ご存知の通り、SQL の多量発行はコストの高い操作です。

例えば、

function get_count_properties($condition) {
    $oRecords = Sample1::where('value', $condition['sample1'])->get();
    $rec = [];
    foreach ($oRecords as $oRecord) {
        $rec[$oRecord->id] = Sample2::where('value', $condition['sample2'])
            ->where('sample1_id', $oRecord->id)->count();
    }
    return $rec;
}

もろに1+N問題が発生しています。

Eager loading の解説にありますが、集合演算をうまく使うことで、この状況を回避できます。

function get_count_properties($condition) {
    $ids = Sample1:where('value', $condition['sample1'])->pluck('id');
    $rec = Sample2::select(DB::raw('count(*) as cnt'), 'sample1_id')
        ->where('value', $condition['sample2'])
        ->whereIn('sample1_id', $ids)
        ->groupBy('sample1_id')
        ->pluck('cnt', 'sample1_id')->toArray();
    return $rec;
}

この実装では2回の SQL 発行で同じ結果が得られます。

レコード数が多いと前者の実装は不利になっていきます。XServer に30万件以上蓄積したテーブルで実測したところ、30秒程度要しました。後者の実行は1秒以内に終了します。

投稿者について
みのしす

小さいときは科学者になろうとしたのに、その時にたまたま身に着けたプログラミングで未だに飯を食っているしがないおじさんです。(年齢的にはもうすぐおじいさん)

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です