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

ist*_*tan 31 ruby sql ruby-on-rails

class User 
has_many :books
Run Code Online (Sandbox Code Playgroud)

我需要一个返回的查询:

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

到目前为止我有什么

User.joins(:books).merge(Book.where(:complete => true))
Run Code Online (Sandbox Code Playgroud)

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

谢谢!

Pan*_*kos 47

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

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
Run Code Online (Sandbox Code Playgroud)

要将其转换为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
Run Code Online (Sandbox Code Playgroud)

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

User.last_book_completed
Run Code Online (Sandbox Code Playgroud)


Mar*_*rom 14

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

在书籍中添加"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
Run Code Online (Sandbox Code Playgroud)

然后,当您保存书籍时,请更新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
Run Code Online (Sandbox Code Playgroud)

现在,为您的查询

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
Run Code Online (Sandbox Code Playgroud)

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

User.last_book_completed
Run Code Online (Sandbox Code Playgroud)

  • 这是迄今为止最简单的答案 (2认同)