Mysql SUM - 如何避免多次对相同的东西求和

时间:2011-10-17 14:42:25

标签: mysql sum

我正在尝试生成债务人的年龄平衡,但是当我必须考虑余额历史记录时,我正在使用MySQL SUM。

我可以通过以下方式获得客户的当前余额:

SELECT SUM(balance) FROM `transaction` WHERE customer_id = 1

起初我想获得旧的平衡 - 即。平衡如何看2011-08-31我认为以下就足够了:

SELECT SUM(balance) FROM `transaction` WHERE customer_id = 1 AND posted <= '2011-08-31'

但上述情况并未考虑自2011-09-01以来至今所发生的任何平衡历史记录。我已将所有平衡历史存储在表格中。因此,如果客户(部分)支付2011-09-06的账单,那么表格将如下所示:

交易:

id | text    | amount | balance | posted
 1 | Invoice | 100.00 |   20.00 | 2011-08-14
 2 | Payment |  80.00 |    0.00 | 2011-09-06

余额历史记录:

id | source | destination | amount | created
 1 |      1 |           2 |  80.00 | 2011-09-06

我以为我已经用以下查询解决了它:

SELECT customer_id, SUM(balance)+SUM(IFNULL(bh_source.amount, 0))-SUM(IFNULL(bh_destination.amount, 0)) AS `current_balance`
FROM `transaction`
LEFT JOIN balancehistory AS bh_source ON `transaction`.id = bh_source.source AND DATE(bh_source.created) > "2011-08-31"
LEFT JOIN balancehistory AS bh_destination ON `transaction`.id = bh_destination.destination AND DATE(bh_destination.created) > "2011-08-31"
WHERE posted <= "2011-08-31"
GROUP BY customer_id

但是当平衡历史变得更加复杂时,即。如下所示 - 它失败了:

交易:

id | text              | amount  | balance | posted
 1 | Invoice           |  100.00 |    0.00 | 2011-08-14
 2 | Payment           |  -80.00 |    0.00 | 2011-09-06
 3 | Payment cancelled |   80.00 |    0.00 | 2011-09-08
 4 | VISA              | -100.00 |    0.00 | 2011-10-10 

余额历史记录:

id | source | destination |  amount | created
 1 |      1 |           2 |   80.00 | 2011-09-06
 2 |      2 |           1 |  -80.00 | 2011-09-08
 3 |      3 |           2 |   80.00 | 2011-09-08
 4 |      1 |           4 | -100.00 | 2011-10-10

我已经在墙上撞了很长时间了,希望你们有一些建议。表格结构未被锁定,因此如果需要,可以更改。

感谢。

更新

我现在看到我已经过多地简化了我的问题 - 但无论如何,谢谢汤姆。 我不是(仅)对当前总数感兴趣,而是想根据截止日期对金额进行分组。我正在尝试创建一个表格,显示应付款项的金额。 IE浏览器。 0-30天,30-90天等:

年龄平衡(使用以下数据的不同参考日期的多个SQL结果):

reference  | before due | 0-30 days | 30-90 days
2011-08-31 |     233.79 |      0.00 |       0.00
2011-09-02 |       0.00 |      0.00 |       0.00
2011-09-07 |       0.00 |    233.79 |       0.00
2011-10-18 |     100.00 |      0.00 |     233.79
2011-10-25 |       0.00 |    100.00 |     233.79
2011-10-28 |       0.00 |      0.00 |       0.00

交易:

id | customer_id | text             | amount  | balance | posted     | due
 1 |           1 | Invoice 1        |  233.79 |    0.00 | 2011-08-17 | 2011-09-01
 2 |           1 | Payment 1        | -233.79 |    0.00 | 2011-09-01 | 2011-09-01
 3 |           1 | Payment rejected |  233.79 |    0.00 | 2011-09-06 | 2011-09-06
 4 |           1 | Reminder         |  100.00 |    0.00 | 2011-09-14 | 2011-09-23
 5 |           1 | Payment 2        | -333.79 |    0.00 | 2011-09-23 | 2011-09-23

余额历史记录:

id | source | destination | amount | created
 1 |      1 |           2 | 233.79 | 2011-09-05
 2 |      2 |           1 | 233.79 | 2011-09-09
 3 |      3 |           2 | 233.79 | 2011-09-09
 4 |      1 |           5 | 233.79 | 2011-10-26
 5 |      4 |           5 | 100.00 | 2011-10-26

以下是我认为可以找到特定日期的年龄平衡的SQL示例:

SELECT SUM(IF("2011-08-31" <= due, balance, 0))
     + SUM(IF("2011-08-31" <= due, IFNULL(bh_source.amount, 0), 0))
     - SUM(IF("2011-08-31" <= due, IFNULL(bh_destination.amount, 0), 0)) AS before_due,
       SUM(IF("2011-08-31" >  DATE(ADDDATE(due, INTERVAL 0 DAY)) AND
              "2011-08-31" <= DATE(ADDDATE(due, INTERVAL 30 DAY)), balance, 0))
     + SUM(IF("2011-08-31" >  DATE(ADDDATE(due, INTERVAL 0 DAY)) AND
              "2011-08-31" <= DATE(ADDDATE(due, INTERVAL 30 DAY)), IFNULL(bh_source.amount, 0), 0))
     - SUM(IF("2011-08-31" >  DATE(ADDDATE(due, INTERVAL 0 DAY)) AND
              "2011-08-31" <= DATE(ADDDATE(due, INTERVAL 30 DAY)), IFNULL(bh_destination.amount, 0), 0)) AS 0_30_days,
       SUM(IF("2011-08-31" >  DATE(ADDDATE(due, INTERVAL 30 DAY)) AND
              "2011-08-31" <= DATE(ADDDATE(due, INTERVAL 90 DAY)), balance, 0))
     + SUM(IF("2011-08-31" >  DATE(ADDDATE(due, INTERVAL 30 DAY)) AND
              "2011-08-31" <= DATE(ADDDATE(due, INTERVAL 90 DAY)), IFNULL(bh_source.amount, 0), 0))
     - SUM(IF("2011-08-31" >  DATE(ADDDATE(due, INTERVAL 30 DAY)) AND
              "2011-08-31" <= DATE(ADDDATE(due, INTERVAL 90 DAY)), IFNULL(bh_destination.amount, 0), 0)) AS 30_90_days
FROM `transaction`
LEFT JOIN balancehistory AS bh_source ON `transaction`.id = bh_source.source AND DATE(bh_source.created) > "2011-08-31"
LEFT JOIN balancehistory AS bh_destination ON `transaction`.id = bh_destination.destination AND DATE(bh_destination.created) > "2011-08-31"
WHERE posted <= "2011-08-31"
GROUP BY customer_id

但它没有按预期工作。任何帮助/建议将不胜感激。

1 个答案:

答案 0 :(得分:0)

如果将发票和付款交易分成两个单独的表,您可能会有更多的乐趣。例如:

invoice
id | customer_id  | invoice_description  | amount | postedOn
 1 |     1        | Services Invoice     | 100.00 | 2011-08-14 12:35:01

payment
id |  customer_id | invoice_id | payment_type        | amount  | madeOn
 1 |      1       |      1     | Payment             | -80.00  | 2011-09-08 08:09:12
 1 |      1       |      1     | Payment Cancelled   |  80.00  | 2011-09-08 08:12:34
 1 |      1       |      1     | VISA Payment        | -100.00 | 2011-10-10 17:22:54

然后,您应该能够在任何时间点获得给定客户的当前余额,方法是获取截至该时间点发给他们的所有发票的总和,并减去截至该时间点收到的付款。相同的发票。因此:

select customer_id,sum(invoice_balance) as totalBalance
from
(
select i.customer_id, i.id as invoice_id,
i.amount - sum(ifnull(p.amount,0)) as invoice_balance
from invoice i
left join (select p.invoice_id,p.amount from payment  where p.madeOn > '2011-09-01') p
on p.invoice_id = i.id
where i.postedOn > '2011-09-01'
group by i.customer_id, i.id, i.amount
) t group by customer_id;

我故意省略了customer_id上的联接,因为我认为不可能(但不大可能)由不同的客户向最初签发发票的客户付款。进一步考虑,您可能希望将支付表上的payment_type列标准化为paymentType表或类似的东西。