0

I am trying to calculate the cumulative profit from all clients and I find a very good reference from here. However, my table consist of more than 1 client that should have their own cumulative profit. Below is the table 'client' that I already have.

| Year | Month | id  | profit | cumulative |
| 2017 |   1   | 123 |  1000  |            |
| 2017 |   2   | 123 |  -200  |            |
| 2017 |   3   | 123 |  500   |            |
| 2017 |   1   | 456 |  500   |            |
| 2017 |   2   | 456 |  100   |            |
| 2017 |   3   | 456 |  200   |            |

If I am using a sql code like this:

SET @csum := 0;
UPDATE client
SET cumulative = (@csum := @csum + profit);

The result I get is like this:

| Year | Month | id  | profit | cumulative |
| 2017 |   1   | 123 |  1000  |    1000    |
| 2017 |   2   | 123 |  -200  |    800     |
| 2017 |   3   | 123 |  500   |    1300    |
| 2017 |   1   | 456 |  500   |    1800    |
| 2017 |   2   | 456 |  100   |    1900    |
| 2017 |   3   | 456 |  200   |    2100    |

What I expect to get is like this:

| Year | Month | id  | profit | cumulative |
| 2017 |   1   | 123 |  1000  |    1000    |
| 2017 |   2   | 123 |  -200  |    800     |
| 2017 |   3   | 123 |  500   |    1300    |
| 2017 |   1   | 456 |  500   |    500     |
| 2017 |   2   | 456 |  100   |    600     |
| 2017 |   3   | 456 |  200   |    800     |

I am trying to group it by Year, Month, and id as well, but it doesn't work. Basically, I want the cumulative sum for every unique client in each month. Do you have any idea how to solve this? Thanks in advance.

Reza Satnaz
  • 101
  • 2
  • 15

3 Answers3

1

I would avoid local variables since the result can be sometimes different then expected and also set based approach can be optimized better by the DBMS. Use subquery or self-join instead:

SELECT c1.*,
       (SELECT SUM(c2.profit)
        FROM client c2
        WHERE (c2.year < c1.year or 
              (c2.year = c1.year and c2.month <= c1.month)) and 
              c2.id = c1.id
       ) AS cumulative_sum
FROM TABLE client c1

therefore in update it can like this

UPDATE client
JOIN
(
   SELECT c1.id, c1.year, c1.month,
       (SELECT SUM(c2.profit)
        FROM client c2
        WHERE (c2.year < c1.year or 
              (c2.year = c1.year and c2.month <= c1.month)) and 
              c2.id = c1.id
       ) AS cumulative_sum
   FROM client c1
) t ON client.id = t.id and
     client.year = t.year and
     client.month = t.month
SET cumulative = t.cumulative_sum

sqlfiddle demo (Thanks @JohnWoo for the data)

Radim Bača
  • 10,646
  • 1
  • 19
  • 33
1

Local variables works correctly only with ORDER BY in query.

SET @csum := 0, @id:=NULL;
UPDATE client
   SET cumulative = (@csum := if(id=@id,@csum,0) + profit), id=(@id:=id)
ORDER BY id, year, month;

Example on sqlfiddle.com

Or more shortly: ... SET cumulative = (@csum := if(id=@id, @csum, 0*(@id:=id) ) + profit). This comare stored ID with current ID, return stored SUM if ID's same, and return 0 (and store new ID) if ID's different.

Mike
  • 1,985
  • 1
  • 8
  • 14
1

You can do this using variables, but you need to be very careful. When using variables, you want all the operations to be in a single statement -- because MySQL does not guarantee the order of evaluation of statements:

SET @csum := 0;
SET @id := -1;

UPDATE client c
    SET cumulative = (CASE WHEN @id = id
                           THEN @csum := @csum + profit
                           WHEN @id := id
                           THEN @csum := profit
                           ELSE @csum := profit
                      END)
    ORDER BY id, year, month;
Gordon Linoff
  • 1,242,037
  • 58
  • 646
  • 786