我有一个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)
但即使有完全支付的发票,它也会返回空数组。
我的代码有问题吗?
答案 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。