Mysql – Query to generate a new column containing the value of the “previous” column

MySQL

I have a MySQL table that has data similar to this:

id   #        current
--   -----    -------
1    item1    50            
2    item2    60            
3    item1    70            
4    item2    40            
5    item2    80            
6    item2    33            
7    item1    55            

I need to analyze the data per each item and compare it to the previous value of the same item.

So how can I write query in order to retrieve the data like shown below?

id   #        current       prev
--   -----    -------       ----
1    item1    50            -
2    item2    60            -
3    item1    70            50
4    item2    40            60
5    item2    80            40
6    item2    33            80
7    item1    55            70

If no, then what is the most efficient way to achieve this?

The only thing that comes to my mind is query all and for loop on the results keeping array for previous value of each item. I am a little worried about performance as this is going to be a big table with a lot of rows and a lot of items. Any other option?

Best Answer

For version 5.x

SELECT id, 
       CASE WHEN @item = item
            THEN @value
            ELSE @value:=NULL
       END prev, 
       @item:=item,
       @value:=current
FROM tablename, (SELECT @item:='', @value:=0) variables
ORDER BY item, id

The query firstly processes all item1 in the order of id growth, and then processes all item2 (due to ORDER BY expression). CASE in prev's expression checks the moment when item1 finished and item2 started, and resets the variable to NULL (because first record for item have no previous record).

For version 8.x

SELECT id, 
       item, 
       current, 
       LAG(current) OVER (PARTITION BY item 
                          ORDER BY id) prev
FROM tablename

PS. Column name `#` replaced with `item`.

PPS. If you need in final sorting by `id` then wrap the query with proper ordering outer query (for 5+) or add ordering clause (for 8+).