class User
has_many :books
我需要一个返回的查询:
最近一本书的用户:complete =>真正。即如果用户的最新书籍具有:complete =>是的,我在结果中不想要它们。
到目前为止我有什么
User.joins(:books).merge(Book.where(:complete => true))
这是一个很有希望的开始,但不会给我我需要的结果。我尝试添加.order("created_on desc").limit(1)
到上面的查询结束,但是当我期待很多时,我最终只得到一个结果。
谢谢!
答案 0 :(得分:40)
如果您不打算使用@ rubyprince的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
要将其转换为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
答案 1 :(得分:14)
这增加了一点开销,但是在重要时节省了复杂性并提高了速度。
添加" most_recent"专栏到书。确保添加索引。
int calculate_the_answer_to_life() { ... }
int calculate_the_answer_to_death() { ... }
std::future<int> fi = std::async(calculate_the_answer_to_life);
std::future<int> fi2 = std::async(calculate_the_answer_to_death);
std::future<int> fi_composite = std::when_all(fi, fi2).then([](auto a, auto b) {
assert(a.is_ready() && b.is_ready());
return a.get() + b.get();
});
int result = fi_composite.get();
然后,当您保存图书时,请更新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
现在,为您的查询
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
答案 2 :(得分:3)
我无法想到在单个查询中执行此操作的方法,但您可以这样做:
User.all.select { |user| user.books.order("created_at desc").limit(1).complete }
答案 3 :(得分:2)
我最近遇到了类似的问题,这就是我解决它的方法:
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'原因,否则每个用户都会出现两次..
答案 4 :(得分:0)
你应该在这里使用范围 - 它们会让你的生活变得简单(这是一个rails3的例子)
在你的book.rb模型中
scope :completed, where("complete = ?", true)
scope :recent, order("created_on ASC")
然后你可以致电User.books.recent.completed
来获得你想要的东西
答案 5 :(得分:0)
基于Pan's answer但是使用左连接而不是内连接,并且没有分组:
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
}