轨道上的红宝石 - 使用 Postgres,计算关联计数



>我有 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,但我需要通过活动记录来完成它,而不是通过像

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 添加一个计数器缓存,但只是想知道没有它我该怎么做。谢谢!

SQL 不允许在 WHERE 子句中使用别名,而只允许使用列名。

作为替代方案,您可以尝试其中之一:

在纯 SQL 中

def self.good_with_spots_left
  where('foos.max_spots > (SELECT Count(*) FROM bars WHERE bars.a_id = foos.id)')
end

或者,使用位或 Ruby(第二个select在 Ruby 中解释,因为它包含一个 &块)

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

当前接受的答案中的第一个解决方案效率低下,因为对foos表中的每一行执行协同相关的子查询。对于此类方案,使用联接是更好的方法。

当前接受的答案中的第二个解决方案不适用于没有任何bars foos

例如:没有任何订单的新产品。

您必须使用 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

foo没有匹配的bars时,LEFT OUTER JOIN将返回NULL bars.foo_id的值。SQL COUNT 操作不喜欢集合中的NULL值,因此我们将NULL值转换为 0

相关内容

  • 没有找到相关文章

最新更新