通过两个“has_many”子表的总和来排序查询?
在我的应用程序中, Invoice has_many item_numbers
和Invoice has_many payments
。 每张发票都有一个余额,它是ItemNumber金额属性的总和,减去付款金额属性的总和。
在发票模型中很容易计算余额,但我正在尝试编写一个按余额对发票进行排序的查询,这在ActiveRecord / SQL中更难实现。
我已成功设法使用以下查询在item_numbers的总数上订购发票(感谢Daniel Rikowski):
Invoice.where(user_id: 1, deleted: false, status: 'Sent') .joins(:item_numbers) .select('invoices.*, sum(item_numbers.amount)') .group('invoices.id') .order('sum(item_numbers.amount) asc') .limit(20)
我试图通过下面的平衡来扩展这个顺序;
Invoice.where(user_id: 1, deleted: false, status: 'Sent') .joins(:item_numbers) .joins("FULL OUTER JOIN payments ON payments.invoice_id = invoices.id") .select("invoices.*, sum(item_numbers.amount_with_gst) - COALESCE(sum(payments.amount), 0)") .group("invoices.id") .order("sum(item_numbers.amount_with_gst) - COALESCE(sum(payments.amount), 0) #{dir}")/
此查询存在两个问题。 首先,它非常丑陋,其次,它不起作用。 我在付款表上使用了完全外部联接,因为并非所有发票都有付款,如果我只使用加入(:付款),则任何没有付款的发票都会从结果中排除。 COALESCE被放在那里处理零金额。
查询接近,但是说有3个item_numbers和1个付款(非常典型的情况),付款金额将减去3次,导致余额远低于实际金额(通常是负余额)。
可能很清楚我的深度如何。 我已经在这个查询中付出了很多努力(大约4个小时的阅读和失败的尝试)并且不能完全确定它。 我的数据库是PostgreSQL。
您的问题是由列乘以引起的。 想象一下,有一个付款和三个属于发票的Item_numbers。 常规联接的结果将是这样的:
| invoice.id | item_number.amount | payment.amount | | 1 | 4 | 5 | | 1 | 7 | 5 | | 1 | 2 | 5 |
因此,sum(payment.amount)将返回15而不是5.要获得正确的总和,您必须直接获取总和:
Invoice.select('invoices.id, (SELECT SUM(item_numbers.amount) from item_numbers WHERE item_numbers.invoice_id = invoices.id) - (SELECT COALESCE(SUM(payments.amount),0) from payments WHERE payments.invoice_id = invoices.id) AS balance').group('invoices.id')
不确定AR语法,但正确的查询将是:
SELECT i.*, COALESCE(n.total, 0) - COALESCE(p.total, 0) AS balance FROM invoices i LEFT JOIN ( SELECT invoice_id, sum(amount) AS total FROM payments GROUP BY invoice_id ) p ON p.invoice_id = i.id LEFT JOIN ( SELECT invoice_id, sum(amount_with_gst) AS total FROM item_numbers GROUP BY invoice_id ) n ON n.invoice_id = i.id WHERE i.user_id = 1 AND i.deleted = false AND i.status = 'Sent' ORDER BY balance;
如果将两个has_many
表连接到基表,则行相互相乘会导致完全任意的结果。 您可以通过在加入基表之前聚合总计来解决这个问题。
此外,我没有在您的查询中看到item_numbers
的连接条件。 这会导致交叉连接 – 除了非常错误之外极其昂贵。 (或者AR是否足够自动从外键关系派生连接条件?如果是,为什么第二个表上的连接条件?) 假设 item_numbers
有一个invoice_id
列就像payments
一样,我修改了它。