Mysql SUM - как избежать суммирования одного и того же материала несколько раз - PullRequest
0 голосов
/ 17 октября 2011

Я пытаюсь сгенерировать возрастной баланс дебиторов, но у меня возникает двойное использование 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

Я долго бился головой о стенуНадеюсь, у вас есть предложения.Структура таблицы не заблокирована, поэтому при необходимости ее можно изменить.

Спасибо.

Обновление:

Теперь я вижу, что слишком сильно упростил свою проблему - но все равно спасибо, Том.Меня (только) не интересует текущая сумма, но вместо этого я хочу сгруппировать суммы на основе их срока оплаты.Я пытаюсь создать таблицу, которая показывает, сколько просроченных платежей.То есть.от 0 до 30 дней, 30-90 дней и т. д.:

1039 * Баланс по возрасту (несколько результатов SQL с использованием различных контрольных дат для следующих данных):

* Транзакции:

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 голосов
/ 17 октября 2011

У вас может быть немного больше радости, если вы разделите свои счета-фактуры и платежные операции на две отдельные таблицы.Например:

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 или к чему-то подобному.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...