如何使用ActiveRecord计算where子句中的子查询记录?

时间:2017-09-18 15:07:36

标签: sql activerecord

给出两个ActiveRecord模型:

class Foo < ApplicationRecord
  has_many :bars
end

class Bar < ApplicationRecord
  belongs_to :foo
end

Bar的属性color可能包含值redgreen,我想 得到foos,其中红色bars比绿色bars更多。

到目前为止,我能够这样做:

red_bars_sql = Bar.select('COUNT(*)').where(color: 'red').where('foo.id = bar.foo_id').to_sql
green_bars_sql = Bar.select('COUNT(*)').where(color: 'green').where('foo.id = bar.foo_id').to_sql

Foo.where("(#{red_bars_sql}) > (green_bars_sql)")

这表现得相当不错。

我想知道是否有替代方法来解决这个问题。还有,有办法吗? 用更少的SQL和更多的ActiveRecord实现这种当前的方法(避免黑客入侵) 在where的{​​{1}}子句中插入sql。

我尝试了一些事情:

Foo

然后尝试分组(class Foo < ApplicationRecord has_many :bars has_many :reds, -> { reds }, class_name: 'Bar' has_many :greens, -> { greens }, class_name: 'Bar' end class Bar < ApplicationRecord belongs_to :foo scope :reds, -> { where(color: 'red') } scope :greens, -> { where(color: 'green') } end ),但我没有正确构建它并且它不起作用。

1 个答案:

答案 0 :(得分:1)

使用SQL,这可以作为一个查询来完成这样的事情(PostgreSQL的例子,其他数据库对条件聚合有类似的东西):

SELECT
  foo.*
FROM foos
INNER JOIN bars ON foos.id = bars.foo_id
GROUP BY foos.id
HAVING 
  COUNT(CASE WHEN bars.color = 'red' THEN 1 END) 
    > COUNT(CASE WHEN bars.color = 'green' THEN 1 END)

如果您更喜欢AR,它仍然会包含很多SQL,但应该是这样的:

Foo.joins(:bars).group(:id)
  .having(
    "COUNT(CASE WHEN bars.color = 'red' THEN 1 END) " \
      "> COUNT(CASE WHEN bars.color = 'green' THEN 1 END)"
  )