0

举个简单的例子,假设一家书店的书籍只有一位作者。这些书通过订单有很多销售。作者可以有很多书。

我正在寻找一种方法来列出按销售排序的作者。由于销售与书籍相关,而不是与作者相关,我该如何做到这一点?

我猜是这样的: Author.order("sales.count").joins(:orders => :sales)

但这会返回无法找到列的错误。

我已经能够通过在 Author 模型中定义它们来连接它们。下面显示了销售的正确计数,但它确实为每个作者 ping 数据库......不好。我宁愿急切地加载它们,但我似乎无法让它正常工作,因为如果我删除 self.id 并将连接分配给@authors,它不会列出任何碰巧有 0 销售额的作者。

    class Author < ActiveRecord::Base

      def sales_count
        Author.where(id: self.id).joins(:orders => :sales).count
      end
    end

更具体地说,如何按计数结果对它们进行排序,以便首先列出最受欢迎的作者?

4

1 回答 1

2

首先,让我们在 Author 类本身上提供所有关联,以保持查询代码简单。

class Author < AR::Base
  has_many :books
  has_many :orders, :through => :books
  has_many :sales, :through => :orders
end

最简单的方法是让您使用groupwith 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 中。

于 2013-04-28T05:01:56.693 回答