MySQL - Subtracting value from previous row, group by
Working with MySQL variables is great, its like inline program variable assignments. First, the FROM clause "declares" the @ variables for you, defaulting to blank. Then query the records in the expected order you want them. It makes a single pass through the data instead of via repeated subqueries which can be time intensive.
For each row read, compare the @lastSN with the SN of the current record. If different, always return 0. If it IS the same, compute the simple difference. Only AFTER that compare is done, set the @lastSN and @lastValue equal to that of the current record for the next records comparison.
select
EL.SN,
EL.Date,
EL.Value, --remove duplicate alias
if( @lastSN = EL.SN, EL.Value - @lastValue, 0000.00 ) as Consumption,
@lastSN := EL.SN,
@lastValue := EL.Value
from
EnergyLog EL,
( select @lastSN := 0,
@lastValue := 0 ) SQLVars
order by
EL.SN,
EL.Date
This should do the trick:
SELECT l.sn,
l.date,
l.value,
l.value - (SELECT value
FROM energylog x
WHERE x.date < l.date
AND x.sn = l.sn
ORDER BY date DESC
LIMIT 1) consumption
FROM energylog l;
See SQLFiddle: http://sqlfiddle.com/#!2/b9eb1/8
A near universal solution is to join the data on to itself, to find the previous record, by including a correlated sub-query in the join condition...
SELECT
ThisLog.*,
COALESCE(ThisLog.Value - PrevLog.Value, 0) AS consumption
FROM
EnergyLog AS ThisLog
LEFT JOIN
EnergyLog AS PrevLog
ON PrevLog.SN = ThisLog.SN
AND PrevLog.Date = (SELECT MAX(Date)
FROM EnergyLog
WHERE SN = ThisLog.SN
AND Date < ThisLog.Date)
This performs best with one index covering both (SN, Date)
.
You can join two rows of the same table like this:
SELECT this.*, prev.*
FROM tbl this
INNER JOIN tbl prev ON prev.id =
(
SELECT max(t.id)
FROM tbl t
WHERE t.id < this.id
)
WHERE ...
So your case will look like:
SELECT this.SN, this.Date, this.Value, (this.Value - prev.Value) AS consumption
FROM EnergyLog this
INNER JOIN EnergyLog prev ON prev.Date =
(
SELECT max(t.Date)
FROM EnergyLog t
WHERE t.Date < this.Date
)