多表发票SUM比较
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了多表发票SUM比较相关的知识,希望对你有一定的参考价值。
假设我在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.
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
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
这代表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
但是......现在我需要把它变成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')
但是失败了,因为在#1这样的订单上,多次付款,它错误地使订单的价格加倍(由于多次加入),显示它仍然是无偿的。我在SQL中遇到了同样的问题,这就是我按照我的方式构建它的原因。
这有什么想法?
您可以使用mysql的group by和having子句获得结果:
纯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)
ActiveRecord查询:
Invoice.joins(:invoice_items, :invoice_payments).group("invoices.id").having("sum(invoice_items.price) < sum(:invoice_payments.amount)")
在Rails中构建更复杂的查询时,通常Arel Really Exasperates Logicians会派上用场
Arel是Ruby的SQL AST管理器。它
- 简化了复杂SQL查询的生成,以及
- 适应各种RDBMS。
以下是基于要求如何实现Arel实现的示例
invoice_table = Invoice.arel_table
# Define invoice_payment_amounts
payment_arel_table = InvoicePayment.arel_table
invoice_payment_amounts = Arel::Table.new(:invoice_payment_amounts)
payment_cte = Arel::Nodes::As.new(
invoice_payment_amounts,
payment_arel_table
.project(payment_arel_table[:invoice_id],
payment_arel_table[:amount].sum.as("amount_paid"))
.group(payment_arel_table[:invoice_id])
)
# Define invoice_item_amounts
item_arel_table = InvoiceItem.arel_table
invoice_item_amounts = Arel::Table.new(:invoice_item_amounts)
item_cte = Arel::Nodes::As.new(
invoice_item_amounts,
item_arel_table
.project(item_arel_table[:invoice_id],
item_arel_table[:price].sum.as("total"))
.group(item_arel_table[:invoice_id])
)
# Define main query
query = invoice_table
.project(
invoice_table[Arel.sql('*')],
invoice_payment_amounts[:amount_paid],
invoice_item_amounts[:total]
)
.join(invoice_payment_amounts).on(
invoice_table[:id].eq(invoice_payment_amounts[:invoice_id])
)
.join(invoice_item_amounts).on(
invoice_table[:id].eq(invoice_item_amounts[:invoice_id])
)
.where(invoice_item_amounts[:total].gt(invoice_payment_amounts[:amount_paid]))
.with(payment_cte, item_cte)
res = Invoice.find_by_sql(query.to_sql)
for r in res do
puts "---- Invoice #{r.id} -----"
p r
puts "total: #{r[:total]}"
puts "amount_paid: #{r[:amount_paid]}"
puts "----"
end
这将使用您提供给问题的示例数据返回与SQL查询相同的输出。输出:
<Invoice id: 2, notes: "An order with 1 items.", created_at: "2017-12-18 21:15:47", updated_at: "2017-12-18 21:15:47">
total: 2.5
amount_paid: 1.25
----
---- Invoice 1 -----
<Invoice id: 1, notes: "An order with 2 items.", created_at: "2017-12-18 21:15:47", updated_at: "2017-12-18 21:15:47">
total: 12.6
amount_paid: 6.1
----
Arel非常灵活,因此您可以将其作为基础,并根据您可能具有的更具体要求优化查询条件。
我强烈建议您考虑在Invoice表中创建一个缓存列(total,amount_paid)并维护它们,以便您可以避免这种复杂的查询。至少total
附加列可以非常简单地创建和填充数据。
以上是关于多表发票SUM比较的主要内容,如果未能解决你的问题,请参考以下文章