Reputation: 723
I have a table like this:
+-----------+------------+---------------+-------------+--------------+
| member_id | balance | amount | new_balance | time |
+-----------+------------+---------------+-------------+--------------+
| 5 | 630 | -30 | 600 | 2017-08-14 |
| 3 | 142 | -68 | 74 | 2017-08-14 |
| 3 | 120 | 22 | 142 | 2017-08-13 |
| 3 | 0 | 120 | 120 | 2017-08-12 |
| 20 | 300 | 324 | 624 | 2017-08-12 |
| 4 | 100 | -30 | 70 | 2017-08-11 |
| 4 | 0 | 100 | 100 | 2017-08-10 |
| 5 | 30 | 600 | 630 | 2017-08-10 |
+-----------+------------+---------------+-------------+--------------+
And what I expect to select is something like:
+-----------+------------+---------------+---------------+-------------+
| member_id | balance | in_amount | out_amount | new_balance |
+-----------+------------+---------------+---------------+-------------+
| 3 | 0 | 142 | 0 | 142 |
| 4 | 100 | 0 | -30 | 70 |
| 5 | 630 | 0 | 0 | 630 |
| 20 | 300 | 324 | 0 | 624 |
+-----------+------------+---------------+---------------+-------------+
Which is the summary within a certain period (2017-08-11 to 2017-08-13).
balance: the balance during the start of the period;
in_amount: the sum of positive amount within the period;
out_amount: the sum of negative amount within the period;
new_balance: the balance at the end of the period;
I have work out a statement like, in order to obtain the income and outcome:
set @start = "2017-08-11 00:00:00";
set @end = "2017-08-13 00:00:00";
SELECT
DISTINCT(member_id),
sum(if(amount>0, amount, 0)) as in_amount,
sum(if(amount<0, amount, 0)) as out_amount,
FROM transaction
WHERE
(time BETWEEN @start and @end)
group by member_id;
However, I have no idea to include the balance and new_balance into the statement. Does someone have any suggestion to me??
Upvotes: 3
Views: 130
Reputation: 4020
Using two sub queries along with a LEFT JOIN
.
The first sub query retrieves the most recent balance. The second sub query sums up in and out amount for each member, using the date parameters.
Using a LEFT JOIN
allows us to get the balance for each member, including those who didn't make transaction between @start
and @end
set @start = "2017-08-11 00:00:00";
set @end = "2017-08-13 00:00:00";
select
lastTransaction.member_id,
lastTransaction.balance,
IFNULL(computeInOut.in_amount, 0) as in_amount,
IFNULL(computeInOut.out_amount, 0) as out_amount,
lastTransaction.balance,
-- balance is the most recent balance + computed in amout - computed out amount
(lastTransaction.balance + IFNULL(computeInOut.in_amount, 0) + IFNULL(computeInOut.out_amount, 0)) as new_balance
FROM
( -- retrieving the most recent balance information prior to @start
SELECT
member_id,
max(time) as mostRecentTransaction,
balance
FROM transaction
WHERE time <= @start
group by member_id
) as lastTransaction
LEFT JOIN
( -- calculating in & out amounts sum based on the date parameters
SELECT
member_id,
sum(if(amount>0, amount, 0)) as in_amount,
sum(if(amount<0, amount, 0)) as out_amount
FROM transaction
WHERE
(time BETWEEN @start and @end)
group by member_id
) as computeInOut
ON (lastTransaction.member_id = computeInOut.member_id)
Result :
+-----------+---------+--------+---------+------------+
| member_id | balance | inflow | outflow | newbalance |
+-----------+---------+--------+---------+------------+
| 4 | 100 | 0 | -30 | 70 |
| 5 | 30| 0 | 0 | 30 |
+-----------+---------+--------+---------+------------+
Members 5 and 20 are not in the result because their first transaction occured after @start, so there is no balance prior to the events (transactions between the 11h and the 13th)
Upvotes: 1
Reputation: 17615
The balance field can be either at the start of the period or in the first date in the period so a sub query with limit should find these and a coalesce can decide which to use. The new balance has to be the last date in the period so a sub query with a desc order by and limit should find these.
set @start = "2017-08-11 00:00:00";
set @end = "2017-08-13 00:00:00";
select member_id,
coalesce(
(select t1.balance from t t1 where t1.member_id = t.member_id and t1.dt = @start order by t1.dt desc limit 1)
,(select t1.balance from t t1 where t1.member_id = t.member_id and t1.dt > @start order by t1.dt asc limit 1)
) balance,
sum(case when amount > 0 then amount else 0 end) inflow,
sum(case when amount < 0 then amount else 0 end) outflow,
(select t1.new_balance from t t1 where t1.member_id = t.member_id and t1.dt <= @end order by t.dt desc limit 1) newbalance
from t
where t.dt between @start and @end
group by member_id
Result
+-----------+---------+--------+---------+------------+
| member_id | balance | inflow | outflow | newbalance |
+-----------+---------+--------+---------+------------+
| 3 | 0 | 142 | 0 | 142 |
| 4 | 100 | 0 | -30 | 70 |
| 20 | 300 | 324 | 0 | 624 |
+-----------+---------+--------+---------+------------+
3 rows in set (0.00 sec)
And 5 drops out because there are no events in the period.
Upvotes: 1