【问题标题】:Rails order by results count of has_many associationRails 按 has_many 关联的结果计数排序
【发布时间】:2016-04-29 17:02:11
【问题描述】:

我是否可以按从子模型 (Jobs) 返回的项目数对结果 (ASC/DESC) 进行排序?

@featured_companies = Company.joins(:jobs).group(Job.arel_table[:company_id]).order(Job.arel_table[:company_id].count).limit(10)

例如:我需要在顶部打印职位最高的公司

【问题讨论】:

    标签: ruby-on-rails ruby-on-rails-3 activerecord


    【解决方案1】:

    类似:

    Company.joins(:jobs).group("jobs.company_id").order("count(jobs.company_id) desc")
    

    【讨论】:

    【解决方案2】:

    如果你希望经常使用这个查询,我建议你使用内置的counter_cache

    # Job Model
    class Job < ActiveRecord::Base
      belongs_to :company, counter_cache: true
      # ...
    end
    
    # add a migration
    add_column :company, :jobs_count, :integer, default: 0
    
    # Company model
    class Company < ActiveRecord::Base
      scope :featured, order('jobs_count DESC')
      # ...
    end
    

    然后像这样使用它

    @featured_company = Company.featured
    

    【讨论】:

    • 好吧,我真的不想为此添加额外的列。
    • 视情况而定。没有完美的解决方案。如果性能和干净的代码优先,添加一列并没有什么坏处。
    • 但是您必须为jobs 表上的每个插入更新companies.jobs_count 表。那是在 2 个不同的表上写 2 次。收获在哪里?
    • 非常有帮助,+1。 AFAICS,唯一缺少的东西:如果数据库有数据,除非你这样做,否则你将从 count=0 开始:Company.find_each { |company| Company.reset_counters(company.id, :jobs) }
    • 这是迄今为止最好的答案,另请参阅ryan.mcgeary.org/2016/02/05/…,了解有关编写允许通过 SQL 极其快速地更新 counter_cache 的迁移的信息。
    【解决方案3】:

    @user24359 正确的应该是:

    Company.joins(:jobs).group("companies.id").order("count(companies.id) DESC")
    

    【讨论】:

      【解决方案4】:

      导轨 5+

      Rails 5 中引入了对左外连接的支持,因此您可以使用外连接而不是 counter_cache 来执行此操作。这样您仍将保留具有 0 个关系的记录:

      Company
        .left_joins(:jobs)
        .group(:id)
        .order('COUNT(jobs.id) DESC')
        .limit(10)
      

      查询的 SQL 等价物是这样的(通过调用 .to_sql 获得):

      SELECT "companies".* FROM "companies" LEFT OUTER JOIN "jobs" ON "jobs"."company_id" = "companies"."id" GROUP BY "company"."id" ORDER BY COUNT(jobs.id) DESC
      

      【讨论】:

      • 这应该是真正有效的答案,如果您删除.limit(10),您可以获得公司有 0 个职位,而其他答案您将错过 0 个职位的公司。
      • 我仍在使用 Rails 4。你能告诉我原始 SQL 吗?
      • 这似乎不适用于关联。例如,如果 SalesPerson 通过交易有许多 Companies 作为客户。那么sales_person.companies.left_joins(:jobs)... 会导致数据库错误“SELECT DISTINCT, ORDER BY 表达式必须出现在选择列表中”。
      【解决方案5】:
      Company.where("condition here...")
             .left_joins(:jobs)
             .group(:id)
             .order('COUNT(jobs.id) DESC')
             .limit(10)
      

      【讨论】:

      • 这与我的答案明显重复,您只是添加了一个额外的 where 声明。
      【解决方案6】:

      添加到 Tan 的答案中。包含 0 个关联

      Company.joins("left join jobs on jobs.company_id = companies.id").group("companies.id").order("count(companies.id) DESC")
      

      默认情况下,joins 使用内连接。我尝试使用 left join 包含 0 关联

      【讨论】:

        【解决方案7】:

        除了答案之外,direct raw SQL 已从 rails 6 中删除,因此您需要将 SQL 包含在Arel 中(如果raw SQL 是安全的含义,则通过安全避免使用用户输入和在此避免SQL injection)。

        Arel.sql("count(companies.id) DESC")

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2012-06-12
          • 2010-10-22
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多