我已经调试了一段时间了。我将问题缩小到了这个范围:
我有一个包含44部电影的本地数据库。运行查询:
Movie.order(:name).joins(:movie_images).offset(40).limit(10).uniq
我按预期检索了最后4部电影。
现在我想按movie_images.created_at
字段而不是movies.name
来排序结果。我必须添加select
子句,因为distinct
要求。
Movie.select("movies.*, movie_images.created_at").order("movie_images.created_at").joins(:movie_images).offset(40).limit(10).uniq
这会按预期返回最后4部电影,但它会再次返回前6部电影(现在是先前加载的重复电影),累加到10的集合limit
。
我的44部电影每部都有2张图片,每张图片的created_at
时间不同。所以我的假设是根据select子句有88个不同的结果:
SELECT DISTINCT movies.*, movie_images.created_at
将offset
设置为80会返回最后8个结果,这证明了我的观点。
但是,如果仍然只选择不同的电影,您将如何通过movie_images.created_at
订购
答案 0 :(得分:2)
您可以使用GROUP BY语句,如下所示:
SELECT movies.id, MAX(movie_images.created_at) mi_created_at
FROM movies JOINS movie_images ON movies.id = movie_images.movie_id
GROUP BY movies.id
ORDER BY mi_created_at
另请参阅Stackoverflow上的this答案,了解PostgreSQL中GROUP BY和DISTINCT语句的差异。
要考虑的另一件事是不同的设计。 Rails在belongs_to
关联上提供touch
选项。这意味着如果子对象发生更改(即正在创建新的电影图像等),它将更新父对象的updated_at
列。
class MovieImage < ActiveRecord::Base
belongs_to :movie, touch: true
end
有了这个,您只需按updated_at
对象的Movie
列进行排序。
Movie.order(updated_at: :asc).offset(40).limit(10)
当然,这也包含更新,例如Movie
对象本身正在更新,或者MovieImage
被破坏等等。如果您纯粹想要考虑创建新的MovieImage
对象,您还可以使用after_create
回调以及Movie
模型上的特定列:
class MovieImage < ActiveRecord::Base
belongs_to :movie
after_create :touch_movie
def touch_movie
movie.update(last_movie_image_created_at: Time.now)
end
end
然后在查询中使用该列:
Movie.order(:last_movie_image_created_at).offset(40).limit(10)
通过这些小的设计更改,我认为它更具可读性和显而易见性,而不是具有无限的不同或分组查询。