通过两个“has_many”子表的总和来排序查询?

在我的应用程序中, Invoice has_many item_numbersInvoice 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一样,我修改了它。