joins オプションとinclude オプションの決定的な違い
railsのfindメソッドのincludeオプションとjoinsオプションの違いに付いてまとめてみた
- 環境
- rails 1.2.3
- DB構成
mysql> DESCRIBE users; +-------+-------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-------+-------------+------+-----+---------+----------------+ | id | int(11) | | PRI | NULL | auto_increment | | name | varchar(40) | YES | | | | +-------+-------------+------+-----+---------+----------------+ 2 rows in set (0.00 sec) mysql> DESCRIBE diaries; +---------+---------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +---------+---------+------+-----+---------+----------------+ | id | int(11) | | PRI | NULL | auto_increment | | user_id | int(11) | | MUL | 0 | | | body | text | YES | | | | +---------+---------+------+-----+---------+----------------+ 3 rows in set (0.00 sec) mysql> DESCRIBE comments; +----------+---------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +----------+---------+------+-----+---------+----------------+ | id | int(11) | | PRI | NULL | auto_increment | | user_id | int(11) | | MUL | 0 | | | diary_id | int(11) | | MUL | 0 | | | body | text | YES | | | | +----------+---------+------+-----+---------+----------------+ 4 rows in set (0.00 sec)
- リレーション
class User < ActiveRecord::Base has_many :diaries has_many :comments end class Diary < ActiveRecord::Base; belongs_to :user has_many :comments end class Comment < ActiveRecord::Base; belongs_to :user belongs_to :diary end
joins
User.find(:all, :joins => "LEFT OUTER JOIN diaries ON diaries.user_id = users.id", :conditions => ["users.id = ?", "1"])
- 発行される SQL
SELECT * FROM users LEFT OUTER JOIN diaries ON diaries.student_id = users.id WHERE users.id = 1
-
- SELECT句には*が発行される
- FROMの後にjoinsで指定した文字列が発行される
- レスポンス
[#<User:0xb6f61e0c @attributes={"id"=>"2", "name"=>"太郎", "user_id"=>"1", "body"=>"joins オプションとinclude オプションの決定的な違い"}>]
-
- Userモデルのオブジェクトの属性"@attributes"として全カラムが格納されて返ってくる
- idカラムの値が上書きされている
include
User.find(:all, :include => [:diaries], :conditions => ["users.id = ?", "1"])
- 発行される SQL
SELECT users.`id` AS t0_r0, users.`name` AS t0_r1, diaries.`id` AS t1_r0, diaries.`user_id` AS t1_r1, diaries.`body` AS t1_r2 FROM users LEFT OUTER JOIN diaries ON diaries.user_id = users.id WHERE users.id = 1
- レスポンス
[#<User:0xb6f61e0c @attributes={"id"=>"1", "name"=>"太郎"}, @diarys=[#<Diary:0xb6f61768 @attributes={"id"=>"2", "user_id"=>"1", "body"=>"joins オプションとinclude オプションの決定的な違い"}>]>]
-
- Userクラスのオブジェクトの属性"@attributes"には、usersテーブルのカラムが格納され、"@diarys"には、Diaryクラスのオブジェクトが格納され、その中には、diariesテーブルのカラムが格納されている
二つのオプションの決定的な違い
- include
- 参照をキャッシュして保存している
- SQL問い合わせ後、関連先のオブジェクトとして準備してくれる
- joins
- 併用
- includeとjoinsは併用が可能
- 注意するべき点
- joinsはSELECT句を生成しない
- includeでエイリアスが貼られてSELECT句に格納されるのは、findの対象とincludeで指定されたテーブルのみ
- つまり、joinsとincludeを併用した場合、joinsで指定して、JOINしたテーブルのカラムはSELECTされない
- 使い分け
- 実務上、includeがとても便利である
- joinsは、そのテーブルのカラムの情報自体は不要であるが、検索条件に追加したい場合などに指定すると良い
- 例えば、
User.find( :all, :joins => "LEFT OUTER JOIN comments ON comments.user_id = users.id", :include => [:diaries], :conditions => ["users.id = ? AND comments.body LIKE ?", "1", "%Rails%"] )
-
-
- 発行される SQL
-
SELECT users.`id` AS t0_r0, users.`name` AS t0_r1, diaries.`id` AS t1_r0, diaries.`user_id` AS t1_r1, diaries.`body` AS t1_r2 FROM users LEFT OUTER JOIN diaries ON diaries.user_id = users.id LEFT OUTER JOIN comments ON comments.user_id = users.id WHERE users.id = 1 AND comments.body LIKE '%Rails%'
最後までお読みいただきありがとうございました。