Rails / SQL:通过检查两个总和来查找发票

时间:2014-08-28 21:58:34

标签: sql ruby-on-rails ruby postgresql join

我有一个Invoice模型has_many行和has_many付款。

Invoice:
  id
  ref

Line:
  invoice_id:
  total (decimal)

Payment:
  invoice_id:
  total(decimal)

我需要找到所有付款发票。所以我做了以下事情:

Invoice.joins(:lines, :payments).having(' sum(lines.total) = sum(payments.total').group('invoices.id')

哪些查询:

SELECT *
FROM "invoices"
INNER JOIN "lines" ON "lines"."invoice_id" = "invoices"."id"
INNER JOIN "payments" ON "payments"."invoice_id" = "invoices"."id"
GROUP BY invoices.id
HAVING sum(lines.total) = sum(payments.total)

但即使有完全支付的发票,它也会返回空数组。

我的代码有问题吗?

2 个答案:

答案 0 :(得分:0)

如果您加入多个具有1:n关系的表,则连接的行可以相互相乘 这个相关的答案有更详细的解释:

为避免这种情况,请在加入之前总计 。这种方式你加入到恰好1(或0)行,没有任何东西成倍增加。不仅正确,而且速度也快得多。

SELECT i.*, l.sum_total
FROM   invoices i
JOIN  (
   SELECT invoice_id, sum(total) AS sum_total
   FROM   lines
   GROUP  BY 1
   ) l ON l.invoice_id = i.id
JOIN  (
   SELECT invoice_id, sum(total) AS sum_total
   FROM   payments
   GROUP  BY 1
   ) p ON p.invoice_id = i.id 
WHERE l.sum_total = p.sum_total;

有意使用[INNER] JOIN,而不是LEFT [OUTER] JOIN。没有任何关税或付款的发票一开始并不感兴趣。因为我们想要"付费"发票。由于缺乏定义和所提供查询的外观,我假设这意味着具有实际行和付款的发票,两者总计相同。

答案 1 :(得分:0)

如果一张发票有一条线,两张付款全额付清:

lines:
id      total   invoice_id
1          30            1

payments:
id      total   invoice_id
1          10            1
2          20            1

然后使用invoce_id将行和付款连接到发票将获得2行,如下所示:

payment_id payment_total   line_id  line_total  invoice_id
1                     10         1          30           1
2                     20         1          30           1

因此line_total的总和不等于payment_total的总和。

要获取所有付费发票,可以使用exists而不是join:

Invoice.where(
       "exists 
        (select 1 from 
          (select invoice_id
           from (select invoice_id,sum(total) as line_total
               from lines
               group by invoice_id) as l
           inner join (select invoice_id,sum(total) as payment_total
               from payments
               group by invoice_id) as p
           on l.invoice_id = p.invoice_id
           where payment_total = line_total) as paid
         where invoices.id = paid.id) ")

支付的sub_query将获得所有已付款的invoice_ids。