あえてEager Loadingしないほうが早い場合

Laravelロゴ

Laravelでリレーション先のレコードを一緒に取得したい場合。リレーション先取得のクエリが大量に発行されてしまうのを防ぐには、withメソッドを使います。
withメソッドを使うことで複数レコードの取得でも1回のクエリでリレーションが取得されます。
これでDBの負荷が減るよねめでたしめでたし・・・とは簡単にいかない場合もあるので注意。

リレーション先のレコードが多かったり複雑なリレーション条件の場合は事情が変わってきます。
特に注意が必要なのはhasManyなモデルで最新の1件だけ取得するような場合がかなり厄介です。
1件とってくる場合はそこまで問題はありませんが、まとめて取得する場合が厄介です。

リレーション元を10件取得したいとすると、リレーション先のデータを取ってくる場合、10件分の条件でリレーション先を全部1回でとってきて”PHP側”で条件にあうレコードを結合しています。
リレーション先はごちゃまぜでとってきてるので結合するためには全データを走査する必要があるので、リレーション先のレコード数が多い場合タイムアウトになってしまいます。

もちろん、リレーション先をgroup byで最新のレコードだけ取得する、というのもおそらく可能だと思いますがなかなか難しい。
ヒントになりそうな記事だけ載せておきます

できなくはないようですがORMでやるのは困難で生SQLを使い必要があるっぽいし、件数が膨大な時にパフォーマンス的に大丈夫なのかというのが疑問。当然DB側の負荷も上がると思います。

ということで、Eager Loadingせずに1レコードずつ引っ張ってきた方が安全です。INDEXも効かせやすいので件数が増えても一発で目的のレコードが取得できます。

まぁ、テーブル設計を変更して、最新のレコードだけのテーブルを別途作る。もしくは、最新フラグを付けるというのも一つの手だとは思います。
前者は正規化の問題があるので整合性が取れなくなる可能性が生じますし、後者は保存時の処理の負荷が問題になりそうです。
ですが、Laravelで実装する場合、booted()メソッドでcreatingかcreatedイベントを実装すれば簡単に実現可能ですし、トランザクションを利用すれば整合性が取れなくなる問題も発生し得ないと思います。

 

PHPフレームワーク Laravel実践開発
アプリケーション開発には、基本のMVC以外の機能実装が非常に重要になります。本書は、既刊『PHPフレームワーク Laravel入門』を読み終わった方に向けて、もっと知りたいという要望の高かったMVC以外の機能実装の手法を解説します。コア機能...
Amazon.co.jp
MySQL徹底入門 第4版 MySQL 8.0対応
MySQL徹底入門 第4版 MySQL 8.0対応

コメント

タイトルとURLをコピーしました