3

Sorry for the real simple question, I just learn PHP & MySQL, I already googling it for more than a week but I didn't found any answer.

I create a simple finance script and the table is like below :

table_a
aid | value
1   | 100
2   | 50
3   | 150

table_b
bid | aid | value
1   | 1   | 10
2   | 1   | 15
3   | 2   | 5
4   | 2   | 10
5   | 3   | 25
6   | 3   | 40

I want the result like this

No | ID | Total | Balance
1  | 1  | 10    | 90
2  | 1  | 25    | 75
3  | 2  | 5     | 45
4  | 2  | 15    | 35
5  | 3  | 25    | 125
6  | 3  | 65    | 85

Can anybody help me with my problem?

Thanks

3 Answers3

2

Try this running total: http://www.sqlfiddle.com/#!2/ce765/1

select  
    bid as no, value,
    @rt := if(aid = @last_id, @rt + value, value) as total,
    @last_id := aid
from table_b b, (select @rt := 0 as x, @last_id := null) as vars
order by b.bid, b.aid;

Output:

| NO | VALUE | TOTAL | @LAST_ID := AID |
|----|-------|-------|-----------------|
|  1 |    10 |    10 |               1 |
|  2 |    15 |    25 |               1 |
|  3 |     5 |     5 |               2 |
|  4 |    10 |    15 |               2 |
|  5 |    25 |    25 |               3 |
|  6 |    40 |    65 |               3 |

Then join to table A, final query:

select x.no, x.aid, x.value, x.total, a.value - x.total as balance
from
(
  select    
    bid as no, aid, value,
    @rt := if(aid = @last_id, @rt + value, value) as total,
    @last_id := aid
  from table_b b, (select @rt := 0 as x, @last_id := null) as vars
  order by b.bid, b.aid
) as x
join table_a a using(aid)

Output:

| NO | AID | VALUE | TOTAL | BALANCE |
|----|-----|-------|-------|---------|
|  1 |   1 |    10 |    10 |      90 |
|  2 |   1 |    15 |    25 |      75 |
|  3 |   2 |     5 |     5 |      45 |
|  4 |   2 |    10 |    15 |      35 |
|  5 |   3 |    25 |    25 |     125 |
|  6 |   3 |    40 |    65 |      85 |

Live test: http://www.sqlfiddle.com/#!2/ce765/1


UPDATE

Not dependent on column bid sorting, running total on grouping will not be impacted: http://www.sqlfiddle.com/#!2/6a1e6/3

select x.no, x.aid, x.value, x.total, a.value - x.total as balance
from
(
  select    
    @rn := @rn + 1 as no, aid, value,
    @rt := if(aid = @last_id, @rt + value, value) as total,
    @last_id := aid
  from table_b b, (select @rt := 0 as x, @last_id := null, @rn := 0) as vars
  order by b.aid, b.bid
) as x
join table_a a using(aid)

Output:

| NO | AID | VALUE | TOTAL | BALANCE |
|----|-----|-------|-------|---------|
|  1 |   1 |    10 |    10 |      90 |
|  2 |   1 |    15 |    25 |      75 |
|  3 |   1 |     7 |    32 |      68 |
|  4 |   2 |     5 |     5 |      45 |
|  5 |   2 |    10 |    15 |      35 |
|  6 |   3 |    25 |    25 |     125 |
|  7 |   3 |    40 |    65 |      85 |

Live test: http://www.sqlfiddle.com/#!2/6a1e6/3

Michael Buen
  • 38,643
  • 9
  • 94
  • 118
  • This answer has the big problem of producing incorrect results if the rows in table_b are not sorted by aid ... try `insert into table_b values (7,1,7);` and then again your query - last line will read `7/1/7/7/93` instead of `7/1/7/32/68` – Eugen Rieck Jun 03 '12 at 11:19
  • @EugenRieck That can easily be rectified ;-) – Michael Buen Jun 03 '12 at 11:29
1
SELECT
   tb.bid as No,
   ta.aid as ID,
   tb.value as Total,
   ta.value-tb.total as Balance
FROM
  table_a AS ta
  INNER JOIN (
    SELECT
      tbx.aid AS aid,
      tbx.bid AS bid,
      tbx.value AS value,
      SUM(tby.value) AS total
    FROM 
      table_b AS tbx
      INNER JOIN table_b AS tby ON tby.aid=tbx.aid AND tby.bid<=tbx.bid
    GROUP BY tbx.bid
    ORDER BY tbx.bid
  ) AS tb ON tb.aid=ta.aid
ORDER BY tb.bid

As @Quassnoi pointed out, this is not very efficient with MySQL. I tried to use a freak join instead of a subquery, as the inner query might be of use in its own right.

Edit

Took some interest in this and found the join version to be twice as fast as the subquery version by @Quassnoi ... anybody having an idea why this would be?

Edit

Answer to the second question (in comment below):

SELECT
  table_a.aid AS aid,
  SUM(table_b.value) AS Total,
  table_a.value-SUM(table_b.value) AS Balance
FROM
  table_a
  INNER JOIN table_b ON table_a.aid=table_b.aid
GROUP BY table_a.aid
Eugen Rieck
  • 64,175
  • 10
  • 70
  • 92
  • Hi sir, sorry to disturb you again. How if I wanna make another page like this [aid|Total|Balance: Value 1|25|75 2|15|35 3|65|85]. Thanks – Agus Darmaputra Jun 03 '12 at 10:55
  • This is not a good answer, this will be ***very slow*** even on few rows. Even if you just have 1,000 rows, it will result to back-and-forth of 500,500 rows. 2,000 rows will result to 2 million and a thousand rows. I'm a fan of this article: http://sqlblog.com/blogs/adam_machanic/archive/2006/07/12/running-sums-redux.aspx – Michael Buen Jun 03 '12 at 11:03
  • 1
    Updated my answer with this second question – Eugen Rieck Jun 03 '12 at 11:03
  • @MichaelBuen This is exactly why Quassnoi and I both pointed out, that this is not efficient - it still is the best answer to the OQ I can think of. I personally would create the table in the surrounding environment (PHP?), not SQL - but this is what was asked. – Eugen Rieck Jun 03 '12 at 11:05
  • By the way, if you want an automatic creation of DDL based on sample data, just copy the OP's sample data. Then paste it to http://sqlfiddle.com **Text to DDL** ツ – Michael Buen Jun 03 '12 at 11:05
  • Wow, great.. Thanks for your Help Mr. Eugen.. You save my day :D – Agus Darmaputra Jun 03 '12 at 11:09
  • Mysql don't have windowing functions (aka analytics functions) yet, but it has one good redeeming factor, variables ツ And a really good one at that, its inline variables is even way better than [Sql Server's](http://stackoverflow.com/questions/10710995/can-this-recursive-solution-be-written-up-into-a-t-sql-query-using-cte-or-over/10712693#10712693). MySQL variables in action: http://stackoverflow.com/questions/10842885/mysql-grouping-while-a-value-is-in-a-range/10843237#10843237 – Michael Buen Jun 03 '12 at 11:29
  • Hi, I hope you still there and read this comment, actually the table not that simple, but since I want to make it quick, I simplify it. And when I apply your code I feel so dizzy. I hope you can help me once more. Here are the more complex table : [table_a :"aid"|Out|Sout|Comp|Comp-detail|subcomp|subcomp-detail|account|account|detail|"value"] when in the previous question I just state "aid" dan "value". Continued below.. – Agus Darmaputra Jun 03 '12 at 15:31
  • Cont.. How to show the other collumn of table_a, in the first question[No|AID|Out|Sout|Comp|Comp-detail|subcomp|subcomp-detail|account|account|detail|Value|Total|Balance] and in the second question [Aid|Out|Sout|Comp|Comp-detail|subcomp|subcomp-detail|account|account|detail|Total|Balance] Thanks – Agus Darmaputra Jun 03 '12 at 15:32
0

You are looking for analytics functions. Unfortunately, MySQL lacks them.

You can implement it in a less efficient way:

SELECT  bid, aid, total, value - total
FROM    (
        SELECT  b.bid, b.aid, COALESCE(a.value, 0) AS value,
                (
                SELECT  SUM(value)
                FROM    b bp
                WHERE   bp.aid = b.aid
                        AND bp.bid <= b.bid
                ) AS total
        FROM    b
        LEFT JOIN
                a
        ON      a.aid = b.aid
        ) q
Quassnoi
  • 413,100
  • 91
  • 616
  • 614
  • You are missing a comma at the end of line 3 – Eugen Rieck Jun 03 '12 at 10:36
  • I try this but I got error message, so what is q in the last line? – Agus Darmaputra Jun 03 '12 at 10:49
  • I got this error : You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '( SELECT SUM(value) FROM b bp ' at line 4: SELECT bid, aid, total, value - total FROM ( SELECT b.bid, b.aid, COALESCE(a.value, 0) AS value ( SELECT SUM(value) FROM b bp WHERE bp.aid = b.aid AND bp.bid <= b.bid ) AS total FROM b LEFT JOIN a ON a.aid = b.aid ) q – Agus Darmaputra Jun 03 '12 at 11:11