我有一个如下所示的架构:
Invoices: | id | ... |
InvoicePayments: | id | invoice_id | amount_cents | ... |
LineItems: | id | invoice_id | unit_price_cents | quantity | ... |
我希望找到未付款的发票,即InvoicePayments的amount_cents总和小于LineItems(quantity * unit_price)之和的发票。我能够通过两个子查询完成此操作,例如:
SELECT prices.id FROM (
SELECT invoices.id, sum(invoice_payments.amount_cents) as paid
FROM invoices
LEFT JOIN invoice_payments ON invoice_payments.invoice_id = invoices.id
GROUP BY invoices.id
) payments JOIN (
SELECT invoices.id, sum(line_items.quantity * line_items.unit_price_cents) as price
FROM invoices
LEFT JOIN line_items ON line_items.invoice_id = invoices.id
GROUP BY invoices.id
) prices
ON payments.id = prices.id
WHERE paid < price OR paid IS NULL;
但是,我正在使用ActiveRecord并希望更简单的东西可以转换成Arel语句;另外,我想将此作为可重复使用的范围,因此我可以通过过滤掉比该日期更新的InvoicePayments来应用其他约束,例如查找在特定日期未付的发票。
有没有办法在没有子查询的情况下完成此操作,以便我可以更轻松地使用Rails并应用灵活的过滤器?
答案 0 :(得分:0)
一种方法是定义视图以封装上面定义的子查询,然后在它们上定义只读模型。
然后,他们可以与发票模型关联,并为您提供大大简化语法的机会。
答案 1 :(得分:0)
SELECT
i.id
COALESCE(SUM(l.quantity * l.unit_price_cents),0) - COALESCE(SUM(p.amounts_cents),0) as UnpaidBalance
FROM
invoices i
LEFT JOIN invoice_payments p
ON i.id = p.invoice_id
AND p.DateColumn <= '2016-01-30'
LEFT JOIN line_items l
ON i.id = l.invoice_id
GROUP BY
i.id
HAVING
COALESCE(SUM(p.amounts_cents),0) < COALESCE(SUM(l.quantity * l.unit_price_cents),0)
特别是通过向许多关系数据库管理系统(如postgressql)添加窗口函数,您很少需要像这样的子查询。使用左连接时,聚合函数将忽略空值,因此只需将左连接组合到同一查询中,您仍然可以获得所需的结果。
您可能会注意到表别名的使用,这使得它更易于阅读,并且为您编写的代码更少。您可以通过在查询中输入表名后面的一个来定义别名。我确实包含了p.DateColumn <= some date
。您可以对查询进行参数化以传递变量,并测试它是否为空,并选择当前日期时间(如果您想要的话)。