我设置了Rails 4范围,以便我找到订购了一定次数的用户。我的问题是,当我将.count放在范围的末尾时,它不会给我一个计数。它产生的底层SQL似乎是错误的。我该如何解决这个问题?
相关架构是一个简单的用户和订单模型。用户模型中的我的范围是:
scope :with_order_count_of, -> (count) {
joins('LEFT OUTER JOIN orders ON users.id=orders.user_id').group('users.id').having("COUNT(orders.id)=#{count}")
}
当我这样调用它时:
User.with_order_count_of(0)
它会让我找到合适的用户。它生成的SQL是:
SELECT users.* FROM "users" LEFT OUTER JOIN ORDERS on users.id = orders.user_id GROUP BY users.id HAVING COUNT(orders.id) = 0;
我的问题是我经常想要这些。但是,这不起作用。它返回{user_ids =>的哈希值0}
User.with_order_count_of(0).count
它使用的SQL是:
SELECT COUNT(*) AS count_all, users.id AS users_id FROM "users" LEFT OUTER JOIN ORDERS on users.id = orders.user_id GROUP BY users.id HAVING COUNT(orders.id) = 0;
这里发生了什么?在Postgres中,COUNT在GROUP BY中应用,而不是计算总行数。我正在摆弄原始SQL,如果我执行这样的嵌套查询,我可以使用SQL获得正确的结果:
SELECT COUNT(*) (SELECT users.* FROM "users" LEFT OUTER JOIN ORDERS on users.id = orders.user_id GROUP BY users.id HAVING COUNT(orders.id) = 0) q;
问题是当我把.count放在最后时,rails没有这样做。我知道我可以做.length而不是.count和ruby会给我正确的答案,但我的表格很大,所以我希望计数发生在SQL级别。
如何修复我的范围,以便为我提供合适的用户:
User.with_order_count_of(0)
这为我提供了用户数量:
User.with_order_count_of(0).count
我离我的答案越来越近了。如果我可以弄清楚如何覆盖rails中的.count
范围,那么我想我可以这样做:
scope :count, -> {
existing_scope_sql = self.connection.unprepared_statement { self.reorder(nil).to_sql }
if existing_scope_sql.downcase.include? 'group by'
self.find_by_sql("SELECT COUNT(*) as count_all FROM (#{existing_scope_sql}) q").first.count_all
else
super
end
}
Rails不会让我这样做。我花了一段时间谷歌搜索,我无法找到方法。任何人都可以帮助完成此解决方案或建议另一种解决方案吗?
答案 0 :(得分:0)
嗯,你可以使用Postgresql的window function概念来实现你想要的。
您目前拥有以下范围:
scope :with_order_count_of, -> (count) {
joins('LEFT OUTER JOIN orders ON users.id=orders.user_id')
.group('users.id')
.having("COUNT(orders.id) = ?", count)
}
# window function is triggered using over()
scope :total_orders_count, ->() {
with_order_count_of.
select("users.id, sum(count(*)) over () as total_count")
}