Rails查询通过仅限于最新记录的关联?

class User has_many :books 

我需要一个返回的查询:

最近一本书的用户:complete => true。 即如果用户最近的书有:complete => false,我不希望它们出现在我的结果中

到目前为止我有什么

 User.joins(:books).merge(Book.where(:complete => true)) 

这是一个很有希望的开始,但没有给我我需要的结果。 我尝试添加.order("created_on desc").limit(1)
到上面的查询结束但是当我期待很多时,我最终只得到一个结果。

谢谢!

如果您不打算使用@ ruby​​prince的ruby解决方案,这实际上是一个比ActiveRecord可以处理的最简单的DB查询,因为它需要一个子查询。 以下是我将如何使用查询完成此操作:

 SELECT users.* FROM users INNER JOIN books on books.user_id = users.id WHERE books.created_on = ( SELECT MAX(books.created_on) FROM books WHERE books.user_id = users.id) AND books.complete = true GROUP BY users.id 

要将其转换为ActiveRecord,我将执行以下操作:

 class User scope :last_book_completed, joins(:books) .where('books.created_on = (SELECT MAX(books.created_on) FROM books WHERE books.user_id = users.id)') .where('books.complete = true') .group('users.id') end 

然后,您可以通过执行以下操作获取具有上次完成的书籍的所有用户的列表:

 User.last_book_completed 

这增加了一点开销,但是在重要时节省了复杂性并提高了速度。

在书籍中添加“most_recent”列。 确保添加索引。

 class AddMostRecentToBooks < ActiveRecord::Migration def self.change add_column :books, :most_recent, :boolean, :default => false, :null => false end add_index :books, :most_recent, where: :most_recent # partial index end 

然后,当您保存书籍时,请更新most_recent

 class Book < ActiveRecord::Base on_save :mark_most_recent def mark_most_recent user.books.order(:created_at => :desc).offset(1).update_all(:most_recent => false) user.books.order(:created_at => :desc).limit(1).update_all(:most_recent => true) end end 

现在,为您的查询

 class User < ActiveRecord::Base # Could also include and preload most-recent book this way for lists if you wanted has_one :most_recent_book, -> { where(:most_recent => true) }, :class_name => 'Book' scope :last_book_completed, -> { joins(:books).where(:books => { :most_recent => true, :complete => true }) end 

这允许您像这样编写它,结果是一个与其他范围一起使用的Relation。

 User.last_book_completed 

我最近遇到了类似的问题,这就是我如何解决它:

 most_recent_book_ids = User.all.map {|user| user.books.last.id } results = User.joins(:books).where('books.id in (?) AND books.complete == ?', most_recent_book_ids, true).uniq 

这样我们只使用ActiveRecord方法(没有额外的SQL),并且可以在考虑用户的任何书籍子集时使用它(第一页,最后一页,最后一本书等等)。 您需要最后一个’uniq’原因,否则每个用户将出现两次..

我想不出一种方法可以在一个查询中执行它,但你可以这样做:

 User.all.select { |user| user.books.order("created_at desc").limit(1).complete } 

你应该在这里使用范围 – 它们会让你的生活变得简单(这是一个rails3的例子)

在你的book.rb模型中

 scope :completed, where("complete = ?", true) scope :recent, order("created_on ASC") 

然后,您可以调用User.books.recent.completed来获得您想要的内容

基于Pan的答案,但是使用左连接而不是内连接,并且没有分组:

 scope :with_last_book_complete, -> { subquery = Book.select(:id) .where("books.user_id = users.id") .order(created_at: :desc).limit(1) joins(<<-SQL).where(books: { complete: true }) LEFT JOIN books ON books.user_id = users.id AND books.id = (#{subquery.to_sql}) SQL }