Post has many Comment
というYii公式ガイドによく登場するモデルを例に記載してみます。
lazy loading(default)
$posts = Post::findAll();
foreach($posts as $post){
foreach($post->comments as $comment){
echo $comment->content;
}
}
最初にpostsがSELECTされ、$post毎にcommentsがSELECTされる。
SELECT回数は(1+post数)となり、いわゆるn+1問題が起きる。
(パフォーマンス上のボトルネックとして顕在化してきて初めて発覚するケースが多いかも。)
使い方によっては省メモリ。
eager loading
イーガーローディングはtogetherオプションにより、2つの手法を使い分けられるようになっている。
together => true
の場合
$posts = Post::findAll(['with'=>['comments' => ['together' => true]]]);
foreach($posts as $post){
foreach($post->comments as $comment){
echo $comment->content;
}
}
最初のSELECTでpostsとcommentsのオブジェクトが全てロードされる。
SELECTは1回だけ。
DBから返却されるデータセットとしては、postsが各コメントにくっついてくるため、
余分なデータがアプリケーションに返却され、冗長かつ無駄が多いが、
クエリ回数が少なくなる等、メリットがある。
Yiiでpostsオブジェクトまで冗長に生成はしてないはず。
together => false
の場合
$posts = Post::findAll(['with'=>['comments' => ['together' => false]]]);
foreach($posts as $post){
foreach($post->comments as $comment){
echo $comment->content;
}
}
最初にpostsがSELECTされ、次にpostsのidに一致する全てのcommentsがSELECTされる。
SELECT回数は2回。オブジェクトは全てロードされる。
DBからは必要なデータセットのみアプリケーションに返却されるためその点は効率的だが、クエリ回数が多くなる。
SQLがFROM comments WHERE comments.post_id IN (1,2,3,4,5,6,7,………)
みたいになるため、
postsの件数によってはIN句にIDが大変な数指定されることがあるので、ちょっと怖い。
おわりに
ここらへんの基本的なところは、
公式ガイドに書いてあるけど、
togetherでこのあたりを使い分けられるのはいいなーと思ったのと、
具体的にどう動くの?というのがなかったのでちょっと書いてみました。
どれがいいかは公式ガイドに記載のある通り、
絶対的な答えはありません。