偏移不选择不同的值

时间:2014-07-01 13:39:11

标签: ruby-on-rails postgresql activerecord

我已经调试了一段时间了。我将问题缩小到了这个范围:

我有一个包含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订购

1 个答案:

答案 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)

通过这些小的设计更改,我认为它更具可读性和显而易见性,而不是具有无限的不同或分组查询。