У меня есть простая таблица data
для данных временных рядов, в основном только отметка времени и значение:
Field, Type, Null, Key, Default, Extra
'id','int(11)','NO','PRI',NULL,'auto_increment'
'channel_id','int(11)','YES','MUL',NULL,''
'timestamp','bigint(20)','NO','',NULL,''
'value','double','NO','',NULL,''
Я разработал несколько сложный запрос для расчета средневзвешенного значения по периодам, в основном вычисляя sum(val x delta time)
. Переменная @prev_timestamp
в основном имитирует функцию LAG()
. Пример:
SELECT
MAX(agg.timestamp) AS timestamp,
COALESCE(
SUM(agg.val_by_time) / (MAX(agg.timestamp) - MIN(agg.prev_timestamp)),
AVG(agg.value)
) AS value
FROM (
SELECT
timestamp,
value,
value * (timestamp - @prev_timestamp) AS val_by_time,
COALESCE(@prev_timestamp, 0) AS prev_timestamp,
@prev_timestamp := timestamp
FROM data
CROSS JOIN (
SELECT @prev_timestamp := NULL
) AS vars
WHERE channel_id=56 AND timestamp >= 1546297161097 AND timestamp <= 1552950000000
ORDER BY timestamp ASC
) AS agg
GROUP BY (timestamp - 1546288811393) >> 23
ORDER BY timestamp ASC
;
Недавно я преобразовал этот запрос из хакерского подхода к переменным MySQL для использования оконных функций:
SELECT
MAX(agg.timestamp) AS timestamp,
COALESCE(
SUM(agg.val_by_time) / (MAX(agg.timestamp) - MIN(agg.prev_timestamp)),
AVG(agg.value)
) AS value
FROM (
SELECT
timestamp,
value,
LAG(timestamp) OVER(ORDER BY channel_id, timestamp ASC) AS prev_timestamp,
value * (timestamp - LAG(timestamp) OVER(ORDER BY channel_id,timestamp ASC)) AS val_by_time
FROM data
WHERE channel_id=56
AND timestamp >= 1546297161097 AND timestamp <= 1552950000000
ORDER BY timestamp ASC
) AS agg
GROUP BY (timestamp - 1546288811393) >> 23
ORDER BY timestamp ASC
;
Недостатком является то, что версия, использующая оконные функции - хотя, по-видимому, чище - работает примерно на 25% медленнее (MariaDB 10.3, Raspi 3).
Есть ли лучший (более производительный) способ написать этот запрос, лучше используя оконную функцию, возможно, избавившись от производной таблицы?