如何通过比较子表上的两个和没有子查询来选择行?

时间:2016-08-12 14:27:05

标签: sql ruby-on-rails postgresql activerecord

我有一个如下所示的架构:

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并应用灵活的过滤器?

2 个答案:

答案 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。您可以对查询进行参数化以传递变量,并测试它是否为空,并选择当前日期时间(如果您想要的话)。