MySQL - вычитание значения из предыдущей строки, группа по
Мне нужно иметь базу значений потребления по предыдущей по номеру SN.
Это мои данные:
ТАБЛИЦА EnergyLog
SN Date Value
2380 2012-10-30 00:15:51 21.01
2380 2012-10-31 00:31:03 22.04
2380 2012-11-01 00:16:02 22.65
2380 2012-11-02 00:15:32 23.11
20100 2012-10-30 00:15:38 35.21
20100 2012-10-31 00:15:48 37.07
20100 2012-11-01 00:15:49 38.17
20100 2012-11-02 00:15:19 38.97
20103 2012-10-30 10:27:34 57.98
20103 2012-10-31 12:24:42 60.83
Это результат, который мне нужен:
SN Date Value consumption
2380 2012-10-30 00:15:51 21.01 0
2380 2012-10-31 00:31:03 22.04 1.03
2380 2012-11-01 00:16:02 22.65 0.61
2380 2012-11-02 00:15:32 23.11 0.46
20100 2012-10-30 00:15:38 35.21 0
20100 2012-10-31 00:15:48 37.07 1.86
20100 2012-11-01 00:15:49 38.17 1.1
20100 2012-11-02 00:15:19 38.97 0.8
20103 2012-10-30 10:27:34 57.98 0
20103 2012-10-31 12:24:42 60.83 2.85
Ответы
Ответ 1
Работа с переменными MySQL велика, это как встроенные назначения переменных программы. Во-первых, предложение FROM "объявляет" переменные @для вас, по умолчанию - пустое. Затем запросите записи в ожидаемом порядке, который вы хотите. Он делает один проход через данные, а не через повторяющиеся подзапросы, которые могут быть интенсивными во времени.
Для каждой прочитанной строки сравните @lastSN с SN текущей записи. Если разные, всегда возвращайте 0. Если это то же самое, вычислите простую разницу. Только ПОСЛЕ того, что сравнение выполнено, установите для @recordsSN и @lastValue равные значения текущей записи для следующего сравнения записей.
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
Ответ 2
Ближайшее универсальное решение состоит в том, чтобы объединить данные для себя, найти предыдущую запись, включив коррелированный подзапрос в условие соединения...
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)
Это лучше всего работает с одним индексом, охватывающим как (SN, Date)
.
Ответ 3
Это должно сделать трюк:
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;
См. SQLFiddle: http://sqlfiddle.com/#!2/b9eb1/8
Ответ 4
Вы можете присоединиться к двум строкам одной таблицы следующим образом:
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 ...
Итак, ваш случай будет выглядеть так:
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
)
Ответ 5
Не могли бы вы попытаться выполнить следующий запрос один раз.
SELECT e1.*,
(SELECT Value
FROM EnergyLog e2
WHERE e2.sn = e1.sn AND e2.date < e1.date
ORDER BY date DESC
LIMIT 1)-l.Value consumption
FROM EnergyLog e1;