上記の記事で、こんなことが書いてありました。
以下に、Railを使用する際に用いられる一般的なパフォーマンス向上方法を紹介しよう。
O/Rマッパーを介さずに直接SQL文を発行する
→ActiveRecord::Base::find_by_sqlメソッド
部分テンプレートキャッシング
→flagment_cache
レコード走査時に、関連先オブジェクトを一括生成する
→eager loading
eager lodingというフレーズは聞いたことあるような気がしたけど、具体的に何かはよくわからなかったので調べました。
eager loadingとは
例えば下記のようなTaskモデルとProjectモデルがあったとして
class Task < ActiveRecord::Base belongs_to :project end class Project < ActiveRecord::Base has_many :tasks end
コントローラ側でtaskをDBより取得して
@tasks = Task.find(:all)
ビューでこんな風に書くと
<% @tasks.each do |task| -%> <%= task.project.name %> <% end -%>
tasksテーブルに入っている列の数だけクエリが生成することになり、パフォーマンス的に大変よろしくありません。先にprojectsテーブルからもデータを取得しておけば、クエリの発行回数を減らすことができます。この、「先にデータを取得しておく」ことを eager loading というらしいです。
Railsでのeager loading実装の変化
eager loadingの言葉の意味を調べる過程で、Railsのeager loadingの実装(ActiveRecord::Base.findの:includeを指定したときの挙動)が2.1を境に変更されたことがわかったのでついでにメモしておきます。*1
Rails2.1以前は、find内で:includeを指定するとLEFT OUTER JOINを使用しクエリを一回だけ実行する形式だったのが、2.1からはテーブルの数と同じ回数のクエリを発行するようになりました。
例
User.all(:include => :company)
とすると、Rails2.1以前はLEFT OUTER JOINをしています。
SELECT `users`.`id` AS t0_r0 `users`.`email AS t0_r1 . . . FROM `users` LEFT OUTER JOIN `companies` ON `companies`.id = `users`.company_id
Rails2.1以降は関連するテーブルごとにクエリを発行しています。
SELECT * FROM `users`; SELECT * FROM `companies` WHERE `companies`.id IN ('1','2', . . . );
Rails2.1以降の:includeの例外
includeで指定した関連のテーブルを:conditions内の条件に使うと2.1以前のLEFT OUTER JOINを使うようになるようです。
例
このように、companiesテーブルを:conditions内の条件で使用すると
User.all(:include => :company, :conditions => "`companies`.`name` LIKE '%google%'")
Rails2.1以降でもLEFT OUTER JOINになってしまいます。
SELECT `users`.`id` AS t0_r0 `users`.`email AS t0_r1 . . . FROM `users` LEFT OUTER JOIN `companies` ON `companies`.id = `users`.company_id WHERE `companies`.`name` LIKE '%google%'
参考
Railscasts - Eager Loading
Module: ActiveRecord::Associations::ClassMethods
includeの注意点。(eager loading) - はらぐろブログラマン
Simpltry » Blog Archive » Rails 2.1 Eager Loading
*1:基本的にSimpltry » Blog Archive » Rails 2.1 Eager Loadingの内容を訳しただけです