【问题标题】:Rails 3 query on condition of an association's countRails 3根据关联计数进行查询
【发布时间】:2011-09-12 21:44:35
【问题描述】:

在带有 mysql 的 Rails 3 中,假设我有两个模型,Customers 和 Purchases,显然是 purchase belongs_to customer。我想找到所有有 2 个或更多订单的客户。我可以简单地说:

Customer.includes(:purchases).all.select{|c| c.purchases.count > 2}

实际上,上面的行对 Customer.all 和 Purchase.all 的大小进行了查询,然后在 ruby​​ 中进行了“选择”类型的处理。在大型数据库中,我更愿意避免在 ruby​​ 中进行所有这些“选择”计算,让 mysql 进行处理,只给我合格客户的列表。这既要快得多(因为 mysql 更适合这样做)并且显着减少了数据库的带宽。

不幸的是,我无法用 rails 中的构建块(在哪里、拥有、组等)中编写代码来实现这一点,类似于(伪代码):

Customer.joins(:purchases).where("count(purchases) > 2").all

我会满足于直接的 MySql 解决方案,尽管我更喜欢在优雅的 rails 框架中解决这个问题。

【问题讨论】:

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


    【解决方案1】:

    这有点冗长,但如果您想要客户 where count = 0 或更灵活的 sql,您可以使用 LEFT JOIN

    Customer.joins('LEFT JOIN purchases on purchases.customer_id = customers.id').group('customers.id').having('count(purchases.id) = 0').length
    

    【讨论】:

      【解决方案2】:

      无需安装 gem 即可使用(尽管 metawhere 很酷)

      Customer.joins(:purchases).group("customers.id").having("count(purchases.id) > ?",0)
      

      【讨论】:

      • 在 Postgrees 中我收到以下错误:PG::UndefinedTable: ERROR: missing FROM-clause entry for table "customer" 这是因为 postgres 以不同的方式实现 SQL 标准,但您能否提供一个适用于 postgres 的示例?谢谢!
      • 太棒了!如果您有 many_to_many 关系(例如,客户通过 customer_purchases 进行了多次购买),您可能没有 customer_purchases.id 列,因此您可能必须使用Customer.joins(:customer_purchases).group("customer.id").having("count(purchases.id) > ?",0)
      • 迄今为止最干净的解决方案,无需添加 gem
      • 只是一个让我印象深刻的便条。如果您使用的是单表继承 (STI),请小心为 group 子句使用表名。
      【解决方案3】:

      在这一点上,关于这些东西的文档相当稀少。如果您要进行更多与此类似的查询,我会考虑使用Metawhere。使用 Metawhere,您可以这样做(或类似的,不确定语法是否完全正确):

      Customer.includes(:purchases).where(:purchases => {:count.gte => 2})
      

      这样做的好处是 MetaWhere 仍然使用 ActiveRecord 和 arel 来执行查询,因此它可以使用“新”rails 3 进行查询的方式。

      此外,您可能不想在最后调用.all,因为这会导致查询ping 数据库。相反,您希望使用延迟加载,并且在您实际需要数据(在视图中,或处理实际数据的其他方法)之前不访问数据库。

      【讨论】:

      • 非常感谢。语法需要稍微改变一下,但 Metawhere 正是我正在寻找的机器人。作为记录,正确的 Rails 代码是:Customer.joins(:purchases).group(:customer_id).having(:customers => (:count[:id] > 2))
      • 对我来说很好。但是对于使用更严格的 SQL 数据库(如 PgSQL)的人来说,组语句需要稍作调整,因为所有列都需要显式声明:.group(Model.column_names.collect { |c| "#{Model.table_name}.#{c}" }.join(",")。这也是启用符号运算符的情况下;没有have语句将是: .having(:purchases => {:count.func(:id).gte => 2})
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-06-19
      相关资源
      最近更新 更多