Search code examples
mysqlsqldata-analysissliding-window

How do I lag columns in MySQL?


Consider the following table:

SELECT id, value FROM table ORDER BY id ASC;
+-----+---------+
| id  | value   |
+-----+---------+
| 12  | 158     |
| 15  | 346     |
| 27  | 334     |
| 84  | 378     |
| 85  | 546     |
+-----+---------+

The id column is auto-incremented but contains gaps. The value column is numeric.

I want to look at the increase in value over time by setting value in relation to the value two rows above. That is for row id=85 I want to set the value of row id=85 (546) in relation to the value of row id=27 (334). The value to be computed for row id=85 is hence 546/334=1.63473.

This is the result I want to achieve:

SELECT id, value, ...;
+-----+---------+---------------------+
| id  | value   | value/lag(value, 2) | (the syntax value/lag(value, 2) is made up)
+-----+---------+---------------------+
| 12  | 158     | NULL                |
| 15  | 346     | NULL                |
| 27  | 334     | 2.11392             | (334/158=2.11392)
| 84  | 378     | 1.09248             | (378/346=1.09248)
| 85  | 546     | 1.63473             | (546/334=1.63473)
+-----+---------+---------------------+

How do I perform such lagging in MySQL?

Please note that the id column contains gaps, so simply joining on the same table with t1.id = t2.id - 2 will not work.


Solution

  • Here is a solution that returns what you want in MySQL

    SET @a :=0;
    SET @b :=2;
    SELECT r.id, r.value, r.value/r2.value AS 'lag'
    FROM
    (SELECT if(@a, @a:=@a+1, @a:=1) as rownum, id, value FROM results) AS r
    LEFT JOIN
    (SELECT if(@b, @b:=@b+1, @b:=1) as rownum, id, value FROM results) AS r2
    ON r.rownum = r2.rownum
    

    MySQL 5.1 doesn't like a self join against a subquery so you have to count rows twice, so not as tidy or scalable as it might be, but it does make specifying the lag simple.

    For readers that use Oracle instead this is much easier

    SELECT id, value, value/lag(value, 2) over (order by id) as lag from results;