ActiveRecordのfindで、:includeと:joinsを使ったときの違いって?
頭の中では:includeと:joinsの違いが分かったつもりになっているが、今まで実際に、内部的な動作を詳しく調べたことはなかった。
というわけで、ちゃんと理解するために実験を行ってみた。
モデルはこうなっている。
class Hoge < ActiveRecord::Base has_many :fugas end
ビューはこんな感じ。
%h1 Listing hoges %table %tr %th Name - @hoges.each do |hoge| %tr %td= h hoge.fugas[0].name
@hoges = Hoge.all(:joins => :fugas)
の場合と、
@hoges = Hoge.all(:include => :fugas)
の場合で、どういった挙動の違いが出るのかを、ログで確認してみた。
development.log
:includeの場合
e[4;36;1mSQL (1.0ms)e[0m e[0;1mSET SQL_AUTO_IS_NULL=0e[0m Processing HogesController#index (for 0:0:0:0:0:0:0:1 at 2009-05-28 14:46:51) [GET] e[4;35;1mHoge Load (7.0ms)e[0m e[0mSELECT * FROM `hoges` e[0m e[4;36;1mFuga Load (1.0ms)e[0m e[0;1mSELECT `fugas`.* FROM `fugas` WHERE (`fugas`.hoge_id IN (1,2)) e[0m Rendering hoges/index Completed in 541ms (View: 450, DB: 9) | 200 OK [http://localhost/hoges]
:joinsの場合
e[4;36;1mSQL (0.0ms)e[0m e[0;1mSET SQL_AUTO_IS_NULL=0e[0m Processing HogesController#index (for 0:0:0:0:0:0:0:1 at 2009-05-28 14:44:41) [GET] e[4;35;1mHoge Load (9.0ms)e[0m e[0mSELECT `hoges`.* FROM `hoges` INNER JOIN `fugas` ON fugas.hoge_id = hoges.id e[0m Rendering hoges/index e[4;36;1mFuga Load (1.0ms)e[0m e[0;1mSELECT * FROM `fugas` WHERE (`fugas`.hoge_id = 1) e[0m e[4;35;1mFuga Load (1.0ms)e[0m e[0mSELECT * FROM `fugas` WHERE (`fugas`.hoge_id = 2) e[0m Completed in 602ms (View: 539, DB: 11) | 200 OK [http://localhost/hoges]
まとめ
:joinsの場合は、hoge.fugas[0].nameを呼び出すたびに、SQLが発行されている。一方、:includeは、最初に関連モデルを全て取得しているため、hoge.fugas[0].nameは、キャッシュからロードされる。
当然、上記のような使い方をする場合は、:includeの方がはるかにパフォーマンスが高い。
:joinsは、関連モデルを検索条件に追加する場合に使うべし。