LoginSignup
2
0

More than 3 years have passed since last update.

Laravel SQL確認してN+1問題考えてみよう!

Last updated at Posted at 2021-03-08

前書き

バックエンドたるものN+1問題しらんくてどうする??っと言われたので、少し調べてみました。
正直、私のコードだめだめやん。って思いました。😭

N+1問題とは??

リレーションで繋がってるテーブル間でのデータを取得し、大抵の場合foreachでデータを回すと思うのですが。
データの取得方法が悪いと、foreachで回す度にクエリが発生しちゃうっていう問題です。(大ざっぱにいうと。)

クエリが多いと何が問題なのか?

クリエの発行数が多いとサーバーの負荷になっちゃって反応遅延や、最悪の場合にはシステムダウンにつながる由々しき問題なのです!

LaravelだとEloquentっていうものがある分、実際に発行されているクエリが見えないのでそこら辺が可視化されにくくなってます。
便利なものも考えものですな。

実際に発行されるクエリを確認する方法。

下記のテーブルがあたっとします。

Database

>>Users table
 id |name|

>>Bbses table
 id | body |user_id |

>>Comments table
 id | comment | bbs_id |

※あとでUserテーブルからデータをとってforeachで回します。
その時のリレーションのイメージは [users->bbses->comments] ってな感じです。

たとえば、Userテーブルから5件データを取得する。

UserController

public function index()
{
    $users = User::limit(5)->get();
    return view('user.index')->with('users', $users);
}

次に、viewでforeachを使って$users を回す。

view
@foreach ($users as $user)  
<p>{{ $user->name }}</p>
<ul>
  @foreach ($user->bbses as $bbs)
  <li>{{ $bbs->body }}</li>
  <li>コメント数{{ count($bbs->comments) }}</li>
  @endforeach
</ul>
@endforeach

すると下記のようなクリエが発行される。


select * from users limit 5
select * from bbses where bbses.user_id = 1
select * from comments where comments.bbs_id = 1
select * from bbses where bbses.user_id = 2
select * from comments where comments.bbs_id = 2
select * from bbses where bbses.user_id = 3
select * from comments where comments.bbs_id = 3
select * from bbses where bbses.user_id = 4
select * from comments where comments.bbs_id = 4
select * from bbses where bbses.user_id = 5
select * from comments where comments.bbs_id = 5

$usersからリレーションで繋がっている情報が取得されるたびにクリエが発行されていることがわかる。
このクリエの発行件数を少なくするために使用するのがwithである。

withを使ってクリエの発行回数を少なくしよう。

UserController
public function index()
{
    $users = User::with('bbses')->limit(5)->get();
    return view('user.index')->with('users', $users);
}
select * from users limit 5
select * from bbses where bbses.user_id in (1, 2, 3, 4, 5)
select * from comments where comments.bbs_id = 1
select * from comments where comments.bbs_id = 2
select * from comments where comments.bbs_id = 3
select * from comments where comments.bbs_id = 4
select * from comments where comments.bbs_id = 5

う〜ん。なにやら、commentsテーブルの方がまだ多い。
こんな時は、.(ドット)を使う!

UserController
public function index()
{
    $users = User::with('bbses.comments')->limit(5)->get();
    return view('user.index')->with('users', $users);
}
select * from users limit 5
select * from bbses where bbses.user_id in (1, 2, 3, 4, 5)
select * from comments where comments.bbs_id in (1, 2, 3, 4, 5)

ちなみにLaravelでのSQLの確認方法

UserController
     $sql = DB::table('users')
            ->where('id', 1)
            ->toSql();
     var_dump($sql);
     exit();

他にも👇でもおっけ〜です。

UserController
     $sql = User::->where('id', 1)->toSql();
     var_dump($sql);
     exit();

上記みたいな感じで書くと確認できます!

残念なことにall();ではtoSql();が使えません😭
この事実にかなりショックを受けました。

N+1問題のことを調べましたが、まだまだひよこ故理解が不十分なので。
なにか間違っている点などありましたら、編集リクエストお願いします😌

2
0
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
2
0