多表发票SUM比较

msc*_*ltz 11 sql ruby-on-rails ruby-on-rails-4

假设我在rails应用程序中有3个表:

发票

id  | customer_id  | employee_id  | notes
---------------------------------------------------------------
1   | 1            | 5            | An order with 2 items.
2   | 12           | 5            | An order with 1 item.
3   | 17           | 12           | An empty order.
4   | 17           | 12           | A brand new order.
Run Code Online (Sandbox Code Playgroud)

invoice_items

id  | invoice_id  | price  | name
---------------------------------------------------------
1   | 1           | 5.35   | widget
2   | 1           | 7.25   | thingy
3   | 2           | 1.25   | smaller thingy
4   | 2           | 1.25   | another smaller thingy
Run Code Online (Sandbox Code Playgroud)

invoice_payments

id  | invoice_id  | amount  | method      | notes
---------------------------------------------------------
1   | 1           | 4.85    | credit card | Not enough 
2   | 1           | 1.25    | credit card | Still not enough
3   | 2           | 1.25    | check       | Paid in full
Run Code Online (Sandbox Code Playgroud)

这代表4个订单:

第一项有2项,总计12.60项.它有两笔付款,总付款金额为6.10.此订单已部分支付.

第二项只有一项,一项付款,总计1.25.此订单全额支​​付.

第三个订单没有物品或付款.这对我们很重要,有时我们会使用这种情况.它被认为是全额付款.

最终订单再次有一个项目,共计1.25,但尚未付款.

现在我需要一个查询:

告诉我所有未完全支付的订单; 也就是说,所有订单都使得项目总数大于付款总额.

我可以在纯sql中做到这一点:

SELECT      invoices.*,
            invoice_payment_amounts.amount_paid AS amount_paid,
            invoice_item_amounts.total_amount AS total_amount
FROM        invoices
LEFT JOIN   (
                SELECT      invoices.id AS invoice_id,
                            COALESCE(SUM(invoice_payments.amount), 0) AS amount_paid
                FROM        invoices
                LEFT JOIN   invoice_payments
                ON          invoices.id = invoice_payments.invoice_id
                GROUP BY    invoices.id
            ) AS invoice_payment_amounts
ON          invoices.id = invoice_payment_amounts.invoice_id
LEFT JOIN   (
                SELECT      invoices.id AS invoice_id,
                            COALESCE(SUM(invoice_items.item_price), 0) AS total_amount
                FROM        invoices
                LEFT JOIN   invoice_items
                ON          invoices.id = invoice_items.invoice_id
                GROUP BY    invoices.id
            ) AS invoice_item_amounts
ON          invoices.id = invoice_item_amounts.invoice_id
WHERE       amount_paid < total_amount
Run Code Online (Sandbox Code Playgroud)

但是......现在我需要把它变成rails(可能作为范围).我可以使用find_by_sql,但然后返回一个数组,而不是ActiveRecord :: Relation,这不是我需要的,因为我想将它与其他范围链接(例如,有一个过期的范围,它使用这个)等

所以原始SQL可能不是正确的方式去这里.....但是什么是?我无法在activerecord的查询语言中执行此操作.

我到目前为止最接近的是:

Invoice.select('invoices.*, SUM(invoice_items.price) AS total, SUM(invoice_payments.amount) AS amount_paid').
  joins(:invoice_payments, :invoice_items).
  group('invoices.id').
  where('amount_paid < total')
Run Code Online (Sandbox Code Playgroud)

但是失败了,因为在#1这样的订单上,多次付款,它错误地使订单的价格加倍(由于多次加入),显示它仍然是无偿的.我在SQL中遇到了同样的问题,这就是我按照我的方式构建它的原因.

这有什么想法?

Vis*_*AIN 9

您可以使用MySQL的group byhaving子句获取结果:

纯MySQL查询:

  SELECT `invoices`.* FROM `invoices` 
  INNER JOIN `invoice_items` ON 
    `invoice_items`.`invoice_id` = `invoices`.`id` 
  INNER JOIN `invoice_payments` ON 
    `invoice_payments`.`invoice_id` = `invoices`.`id` 
  GROUP BY invoices.id 
    HAVING sum(invoice_items.price) < sum(invoice_payments.amount) 
Run Code Online (Sandbox Code Playgroud)

ActiveRecord查询:

Invoice.joins(:invoice_items, :invoice_payments).group("invoices.id").having("sum(invoice_items.price) < sum(:invoice_payments.amount)")
Run Code Online (Sandbox Code Playgroud)