limitの値によってクエリの結果が異なる
先日、"関連付けたモデルの属性で、関連元モデルをソートする"というエントリを書きましたが、これ、一見うまくいっているように見えるだけで、所望の結果が得られない場合があることがわかりました。
Model.allで全レコードを取得したときにしか正しい結果が得られません。
paginationやlimitを使って取得レコード数を制限している場合は、レコードの内容によっては所望の結果が得られない場合があります。
結論から先に言うと、:include ではなく :joinsを使用するというのが正解のようです。
こんな感じ。
default_scope :joins => :report, :group=> "spots.id", :order => "reports.created_at DESC"
SQLを直接発行してうまくいくクエリをActivRecordに書きなおしただけですが、これでうまくいきました。
さて、うまくいかない状況をもう少し詳しく説明します。
先日のエントリで紹介した設定は下記の通り。
class Spot < ActiveRecord::Base has_many :report default_scope :include => :report, :order => "reports.created_at DESC" end class Report < ActiveRecord::Base belongs_to :spot end
これで、spot.all すると、新しいReportを持っている順にspotがsortされて取得できます。
結果も正しい。
でも spot.limit(8)とかやると、spot.allのときと順番が違ったり、取得できるレコードまで違ったりするのです。
生成されたクエリを直接SQLで発行すると、limitが付いている場合でも正しい結果が取得できたりと、わけがわかりません。
色々と実験したのですが、:includeの場合は、limitで指定したレコード数分のspotレコードに紐付いているreportだけをロードしてきて、その中でソートをかけるという動作をしているように見えます。
なので、allじゃないと正しい結果にならない場合があるようです。
SQLちゃんと勉強していればわかる内容なのでしょうが、なんでこんなことになるのかよくわかっておりません。。。