我正在使用RoR 4.2.4尝试创建一个索引页面,其中每一行显示一些买方信息,三个值从关联表中求和。我觉得这种查询必须一直发生,我只是错过了一些非常简单的事情。
class Buyer < ActiveRecord::Base
has_many :wins
has_many :pledges
has_many :payments
胜利,承诺和付款属于买方。
这有效:
@buyers = Buyer.joins("LEFT OUTER JOIN pledges on buyers.id = pledges.buyer_id")
.where(event_id: @event.id)
.select("buyers.*, sum(pledges.amount) as pledges_total")
.group('buyers.id')
.order('buyers.last_name')
我得到了买家和买家名单.pledges_total给了我们承诺的总和。
但在同一个查询中,我还希望获得总和(wins.amount)和sum(payments.amount)。
这个DOESN&#39; T工作:
@buyers = Buyer.joins("LEFT OUTER JOIN wins on buyers.id = wins.buyer_id")
.joins("LEFT OUTER JOIN pledges on buyers.id = pledges.buyer_id")
.joins("LEFT OUTER JOIN payments on buyers.id = payments.buyer_id")
.where(event_id: @event.id)
.select("buyers.*, sum(wins.price) as wins_total, sum(pledges.amount) as pledges_total, sum(payments.amount) as payments_total")
.group('buyers.id')
.order('buyers.last_name')
我得到了奇怪的错误值,让我觉得我在加入后总结。但我真的不确定,我也不知道怎么做。我确信我真的很惊讶这是多么简单。
感谢您的帮助。
答案 0 :(得分:1)
对于您拥有的多个JOIN,SUM将无法正常工作。尝试将该操作移动到子查询中,而不是对整个结果集进行求和。
@buyers = Buyer
.where(event_id: @event.id)
.select(<<-SELECT)
buyers.*,
(SELECT SUM(wins.price) FROM wins WHERE wins.buyer_id = buyers.id) as wins_total,
(SELECT SUM(pledges.amount) FROM pledges WHERE pledges.buyer_id = buyers.id) as pledges_total,
(SELECT SUM(payments.amount) FROM payments WHERE payments.buyer_id = buyers.id) as payments_total
SELECT
.group('buyers.id')
.order('buyers.last_name')
请注意,不再需要原始查询中的JOIN。
以下是在查询中有多个JOIN时,为什么SUM不正确的说明。
假设您有以下数据:
# SELECT * FROM buyers;
id │ event_id
─────┼───────────
1 │ 1
# SELECT * FROM wins;
id │ buyer_id │ price
─────┼──────────┼───────
2 │ 1 │ 10
3 │ 1 │ 20
# SELECT * FROM pledges;
id │ buyer_id │ amount
─────┼──────────┼────────
4 │ 1 │ 30
5 │ 1 │ 40
SQL join返回给定记录集的笛卡尔积。这意味着连接的结果可能包含来自单个元组(行)的重复值。在下面的示例中,我们可以看到赢得的每个元组和承诺重复两次。 SQLFiddle
# SELECT buyers.id, wins.id AS wins_id, wins.price AS wins_price, pledges.id AS pledges_id, pledges.amount AS pledges_amount FROM buyers
# INNER JOIN wins ON wins.buyer_id = buyers.id
# INNER JOIN pledges ON pledges.buyer_id = buyers.id;
id │ wins_id │ wins_price │ pledges_id │ pledges_amount
─────┼─────────┼────────────┼────────────┼────────────────
1 │ 2 │ 10 │ 4 │ 30
1 │ 2 │ 10 │ 5 │ 40
1 │ 3 │ 20 │ 4 │ 30
1 │ 3 │ 20 │ 5 │ 40
我们可以轻松回顾 wins 和承诺表,看看赢取价格的总和等于30,认捐总额等于70.但是,如果我们按ID(buyer.id)分组并执行总和,然后我们最终得到的错误值是它们应该是的两倍! SQLFiddle
# SELECT buyers.id, sum(wins.price) AS wins_total, sum(pledges.amount) AS pledges_total FROM buyers
# INNER JOIN wins ON wins.buyer_id = buyers.id
# INNER JOIN pledges ON pledges.buyer_id = buyers.id
# GROUP BY buyers.id;
id │ wins_total │ pledges_total
─────┼────────────┼───────────────
1 │ 60 │ 140
您可以看到使用子选项会在此SQLFiddle中返回正确的结果。
在您需要总结相关表的值时,是不是会出现这种情况?
是的,这是一个常见的问题。
这是人们做的吗?
我做到了。 :)
或者是否有一种更聪明的完全不同的方法? 只要您拥有良好的索引,子查询方法就可以很好地处理大量数据。在像这样的子查询成为一个重大问题之前,您可能会遇到其他性能问题。
但是,作为计算每个查询的价格和金额总和的替代方法,您可以缓存每个买方的总计值。对该确切主题进行了快速搜索this SO question。缓存增加了复杂性,有时可能很难。您需要评估是否真的需要来缓存值以及是否值得付出努力。我向您介绍的问题显示了如何使用ActiveRecord进行缓存。也可以在数据库中设置执行相同操作的触发器(可能更有效)。