0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

N+1問題回避って何? リレーショナルデータベース Laravel

Posted at

いつも通りコントローラーでリレーション使いながらお仕事をしていた時。

view側を見ると
こんなコードが

<tr>
    <th colspan="4" rowspan="2">果物を収穫した農園主</th>
    <th colspan="2">農園の所在地</th>
    <td colspan="8">{{ $fruit->field->owner->region }}{{ $fruit->field->owner->farm_address }}</td>
</tr>

コントローラーを見ると

$fruit = Work::where('orchard_id', $orchard->id)
             ->whereNull('rotten_at')
             ->with('branch')
             ->with('field')
             ->with('basket')
             ->with('basket.seed')
             ->with('picker')
             ->with('customer')
             ->findOrFail($fruitId);

withでとってきていないな。
ん?これでも取得できるんだ「へー」となっておりました。

損で調べてみたら
N+1問題を回避するためにはコントローラーでまとめたほうが良いよ!

と書いてある。

何じゃ?
ということでもうちょっと調べてみた。

N+1ってまずなに

パターン紹介

ユーザー一覧を取得する。

SELECT * FROM users;

→100人いました!!!

その後各ユーザーの投稿件数を数えるためには100回クエリの実行が必要になる。

SELECT * FROM posts WHERE user_id = 1;
SELECT * FROM posts WHERE user_id = 2;
...
SELECT * FROM posts WHERE user_id = 100;

結果

合計ユーザー一覧で1回、ユーザーごとの投稿数を取得するのに100回のSQLを実行した。

欲しいのはユーザーごと(100人分)の投稿数。
クエリの実行が合計101回となるということでN+1問題ということらしい。

この数が多くなればなるほどどんどんパフォーマンスが落ちるよ〜ということ。

それを解決するのがいつもコントローラーでやっていたあれ。

$users = User::with('posts')->get();

これでユーザーと投稿をひとまとめで取得できる。
ユーザーを取得するクエリとこのクエリの合計2回で済むってことらしい。

このwithメソッドはEager loadingというらしい。

SQL文を利用すると

$users = User::select('users.*')
    ->leftJoin('posts', 'users.id', '=', 'posts.user_id')
    ->groupBy('users.id')
    ->selectRaw('count(posts.id) as post_count')
    ->get();

つまりviewに記載していたのはクエリを余分に実行してしまっていた。

なので

 $fruit = Work::where('orchard_id', $orchard->id)
                 ->whereNull('rotten_at')
                 ->with('branch')
                 ->with('field')
                 ->with('basket')
                 ->with('basket.seed')
                 ->with('picker')
                 ->with('customer')
                 ->with('owner') //追加
                 ->findOrFail($fruitId);

としてこう書き換えた

   <tr>
        <th colspan="4" rowspan="2">果物を収穫した農園主</th>
        <th colspan="2">農園の所在地</th>
        <td colspan="8">{{ $fruit->owner->region }}{{ $fruit->owner->farm_address }}</td>
    </tr>

これでクエリの実行回数は減ったんだろう。
ローカルの数が少ないところでやっているので速度はわからないが。

クエリって何?

クエリクエリ言ってなんとなく使っていたけど
クエリはDBに質問する命令文のことを言うらしい。

SELECTやINSERT、UPDATE、DELETEのことだ。

機能の追加作業をやっているけど楽しいな。
そしてこういった要件決める人大変そう。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?