同じにやっても動かない

書いてある通りやっているのに動かないのです

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ちゃんと勉強していればわかる内容なのでしょうが、なんでこんなことになるのかよくわかっておりません。。。