tags:

views:

283

answers:

4

I have a table that looks like this:

id   count
1    100
2    50
3    10

I want to add a new column called cumulative_sum, so the table would look like this:

id   count  cumulative_sum
1    100    100
2    50     150
3    10     160

Is there a MySQL update statement that can do this easily? What's the best way to accomplish this?

+6  A: 

Using a correlated query:


  SELECT t.id,
         t.count,
         (SELECT SUM(x.count)
            FROM TABLE x
           WHERE x.id <= t.id) AS cumulative_sum
    FROM TABLE t
ORDER BY t.id

Using MySQL variables:


  SELECT t.id,
         t.count,
         @running_total := @running_total + t.count AS cumulative_sum
    FROM TABLE t
    JOIN (SELECT @running_total := 0) r
ORDER BY t.id

Note:

  • The JOIN (SELECT @running_total := 0) r is a cross join, and allows for variable declaration without requiring a separate SET command.
  • The table alias, r, is required by MySQL for any subquery/derived table/inline view

Caveats:

  • MySQL specific; not portable to other databases
  • The ORDER BY is important; it ensures the order matches the OP and can have larger implications for more complicated variable usage (IE: psuedo ROW_NUMBER/RANK functionality, which MySQL lacks)
OMG Ponies
+1. That is so obvious, I can't believe I've never thought of it before! That is nice!
David Stratton
+1: Nice solution... Added a comma after `t.count`.
Daniel Vassallo
I would add "ORDER BY t.id ASC" to the main query, just to make sure it'll always work
Wacek
My first thought also was to add ORDER BY. But it does not matter. Until addition turns into non-associative, at least :)
Dercsár
@OMG Poines: I think you need to use a `SELECT` in the `JOIN (SELECT @running_total := 0)` part of the variables example.
Daniel Vassallo
+1  A: 
UPDATE t
SET cumulative_sum = (
 SELECT SUM(x.count)
 FROM t x
 WHERE x.id <= t.id
)
Dercsár
Although the OP did ask for an update, this is denormalized and will probably be inconvenient to maintain correctly.
Matthew Flaschen
+1  A: 

You could also create a trigger that will calculate the sum before each inser

delimiter |

CREATE TRIGGER calCumluativeSum  BEFORE INSERT ON someTable
  FOR EACH ROW BEGIN

  SET cumulative_sum = (
     SELECT SUM(x.count)
        FROM someTable x
        WHERE x.id <= NEW.id
    )

    set  NEW.cumulative_sum = cumulative_sum;
  END;
|

I have not tested this

Greg
+3  A: 

If performance is an issue, you could use a MySQL variable:

set @csum := 0;
update YourTable
set cumulative_sum = (@csum := @csum + count)
order by id;

Alternatively, you could remove the cumulative_sum column and calculate it on each query:

set @csum := 0;
select id, count, (@csum := @csum + count) as cumulative_sum
from YourTable
order by id;

This calculates the running sum in a running way :)

Andomar
Use a cross join to define the variable without needing to use `SET`.
OMG Ponies
+1: Much better then the current top answer IMHO.
James
My table has 36 million records, so this was really helpful to speed things up!
Kirk