举一个简单的例子,假设一家书店有一本书有一位作者。这些书通过订单有很多销售。作者可以有很多书。
我正在寻找一种方法来列出按销售订购的作者。由于销售与书籍有关,而与作者无关,我如何才能实现这一目标?
我猜是这样的:
Author.order("sales.count").joins(:orders => :sales)
但是返回一个列无法找到错误。
我已经能够通过在作者模型中定义它来连接它们。以下显示正确的销售计数,但它确实为每个作者ping数据库......不好。我更愿意加载它们,但我似乎无法让它正常工作,因为如果我删除self.id并将连接分配给@authors,它将不会列出任何碰巧有0销售的作者。 / p>
class Author < ActiveRecord::Base
def sales_count
Author.where(id: self.id).joins(:orders => :sales).count
end
end
更具体地说,如何通过计数结果对它们进行排序,以便我能够首先列出最受欢迎的作者?
答案 0 :(得分:2)
首先,让我们在Author类本身上拥有所有关联,以保持查询代码的简单。
class Author < AR::Base
has_many :books
has_many :orders, :through => :books
has_many :sales, :through => :orders
end
最简单的方法是让group
与count
一起使用,它会以{author-id: count}
形式为您提供哈希:
author_counts = Author.joins(:sales).group("authors.id").count
=> {1 => 3, 2 => 5, ... }
您现在可以使用author_counts
哈希对作者进行排序并查找计数(没有销售的作者将返回nil
):
<% Author.all.sort_by{|a| author_counts[a.id] || 0}.reverse.each do |author| %>
<%= author.name %>: <%= author_counts[author.id] || 0 %>
<% end %>
<强>更新强>
另一种方法是使用ar_outer_joins gem,它可以解决使用includes
生成LEFT JOIN的限制:
authors = Author.outer_joins(:sales).
group(Author.column_names.map{|c| "authors.#{c}").
select("authors.*, COUNT(sales.id) as sales_count").
order("COUNT(sales.id) DESC")
现在您的视图可能如下所示:
<% authors.each do |author| %>
<%= author.name %>: <%= author.sales_count %>
<% end %>
此示例演示了LEFT JOIN在您不能(或特别不希望)急切加载其他关联的情况下的用处。我不知道默认情况下为什么outer_joins
未包含在ActiveRecord中。