Mysql Rails 3查询关联的计数条件

Mysql Rails 3查询关联的计数条件,mysql,ruby,ruby-on-rails-3,Mysql,Ruby,Ruby On Rails 3,在使用mysql的Rails3中,假设我有两个模型,客户和购买,显然购买属于客户。我想找到所有有2个或更多订单的客户。我可以简单地说: Customer.includes(:purchases).all.select{|c| c.purchases.count > 2} 但实际上,上面这行查询Customer.all和Purchase.all的大小,然后在ruby中进行选择类型处理。在大型数据库中,我更希望避免在ruby中进行所有这些选择计算,让mysql进行处理,只给我合格客户的列表。

在使用mysql的Rails3中,假设我有两个模型,客户和购买,显然购买属于客户。我想找到所有有2个或更多订单的客户。我可以简单地说:

Customer.includes(:purchases).all.select{|c| c.purchases.count > 2}
但实际上,上面这行查询Customer.all和Purchase.all的大小,然后在ruby中进行选择类型处理。在大型数据库中,我更希望避免在ruby中进行所有这些选择计算,让mysql进行处理,只给我合格客户的列表。这两种方法都要快得多,因为mysql更适合这样做,并且大大减少了数据库的带宽

不幸的是,我无法用railswhere、having、group等中的构建块来想象代码。为了实现这一点,psudo代码行中出现了一些东西:

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

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

关于这方面的文档目前相当稀少。如果您还要进行类似的查询,我会考虑使用。使用Metawhere,您可以执行此操作或类似操作,但不确定语法是否完全正确:

Customer.includes(:purchases).where(:purchases => {:count.gte => 2})
这样做的好处是MetaWhere仍然使用ActiveRecord和arel来执行查询,因此它与“新”rails 3的查询方式配合使用


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

尽管metawhere很酷,但无需安装gem即可实现此功能

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

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


非常感谢。语法需要改变一点,但Metawhere正是我要找的机器人。作为记录,正确的Rails代码是:Customer.joins:purchases.group:Customer\u id.having:customers=>:count[:id]>2对我来说很好。但是对于使用更严格的SQL数据库(如PgSQL)的人来说,group语句需要稍微调整一下,因为所有列都需要显式声明:.groupModel.column_names.collect{c}{Model.table_name}.{c}.join,。这也是启用符号运算符的情况;如果没有having语句,则在Postgrees中会出现以下错误:PG::UndefinedTable:error:missing FROM子句条目for表customer这是因为postgres以不同的方式实现SQL标准,但是您能否提供一个在postgres中工作的示例?谢谢伟大的如果您有多对多关系,请稍加修改,例如,Customer通过Customer\u purchases进行了多次购买您可能没有Customer\u purchases.id列,因此您可能必须使用Customer.joins:Customer\u purchases.groupcustomer.id.havingcountpurchases.id>,0迄今为止最干净的解决方案,不需要再加一张便条,只是一张吸引我的便条。如果您使用的是单表继承STI,那么请小心使用group子句的表名。
Customer.joins('LEFT JOIN purchases on purchases.customer_id = customers.id').group('customers.id').having('count(purchases.id) = 0').length