我有2个模特
class Foo < ActiveRecord::Base
# columns are
# max_spots
has_many :bars
end
class Bar < ActiveRecord::Base
# columns are
# a_id
belongs_to :foo
end
我需要得到max_spots大于与之关联的条数的所有Foos但我需要通过活动记录来完成,而不是通过每个Foos来完成
class Foo
#bad
def self.bad_with_spots_left
all.select do |foo|
foo.max_spots - foo.bars.count > 0
end
end
#good but not working
def self.good_with_spots_left
joins(:bars).select('COUNT(bars.id) AS bars_count').where('max_spots - bars_count > 0')
end
end
我知道我可以为foo添加一个计数器缓存,但只想知道如何在没有它的情况下做到这一点。谢谢!
答案 0 :(得分:1)
SQL不允许在WHERE子句中使用别名,只允许使用列名。
作为替代方案,您可以尝试其中一种:
在纯SQL中
def self.good_with_spots_left
where('foos.max_spots > (SELECT Count(*) FROM bars WHERE bars.a_id = foos.id)')
end
或者,有点或红宝石(第二个select
在ruby中解释,因为它包含&amp;块)
def self.good_with_spots_left
joins(:bars).select('foos.*', COUNT(bars.id) AS bars_count').group('bars.a_id').select{|foo| foo.max_spots > foo.bars_count}
end
答案 1 :(得分:1)
当前接受的答案中的第一个解决方案效率低,因为对foos
表中的每一行执行了相关的子查询。对于这种情况,使用连接是一种更好的方法。
当前接受的答案中的第二个解决方案对foos
没有任何bars
不起作用。
例如:没有任何订单的新产品。
您必须使用LEFT外部联接来解决此问题。除此之外,可以使用having
子句完成计数比较。这样就可以在数据库中处理整个操作。
def self.good_with_spots_left
joins("LEFT OUTER JOIN bars bars ON bars.foo_id = foos.id").
group('bars.foo_id').
having("foos.max_spots > COUNT(COALESCE(bars.foo_id, 0))")
end
注意:
COALESCE
命令返回第一个非空输入。此命令与SQL92兼容,因此它可以跨数据库工作。
为什么我们使用COALESCE
?
当LEFT OUTER JOIN
没有匹配的NULL
时,bars.foo_id
会为bars
返回foo
值。 SQL COUNT
操作不喜欢集合中的NULL
值,因此我们将NULL
值转换为0
。