给出两个ActiveRecord模型:
class Foo < ApplicationRecord
has_many :bars
end
class Bar < ApplicationRecord
belongs_to :foo
end
Bar
的属性color
可能包含值red
和green
,我想
得到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
),但我没有正确构建它并且它不起作用。
答案 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)"
)