Сгруппированные по времени оконные функции в масштабе времениb/postgres

#sql #postgresql #timescaledb

Вопрос:

У меня есть гипертекстовая таблица в масштабе времени, которая называется prices , которая выглядит следующим образом:

тс инструментИд ценность
08:00:01 A 100
08:00:01 B 200
08:00:02 B 205
08:00:04 A 95
08:00:06 C 300
08:00:07 A 90

Где каждая строка представляет собой измерение ( value ) в заданную метку времени ( ts ) для данного прибора ( instrumentId ). Каждый инструмент поступает в разное время, и у меня нет гарантии, что у меня будет точка данных для данного инструмента в заданное время.

Моя гипертаблица использует столбец временной метки в качестве индекса времени, и для дополнительного контекста у меня есть ~200 миллионов строк, охватывающих несколько лет субсекундных данных для десятков инструментов.

Я хочу иметь возможность генерировать 5-секундные невзвешенные исторические средние значения для каждого инструмента для каждого измерения, которое у меня есть. Для каждой строки в моей таблице я хочу иметь возможность найти все строки, в которых используется один и тот же инструмент, и иметь метку времени между (row_timestamp, row_timestamp - 5 seconds) ними (этот набор будет включать строку, для которой мы генерируем данные). Затем я хотел бы взять среднее значение этого, но разумно, что я хотел бы взять и другие статистические данные из этого (stdev, сумма и т. Д.).

Результат этого запроса для приведенной выше таблицы примера будет выглядеть следующим образом:

тс инструментИд значение avg_5s_window_ значение
08:00:01 A 100
08:00:01 B 200
08:00:02 B 202.5
08:00:04 A 97.5
08:00:06 C 300
08:00:07 A 92.5

Я могу добиться этого небольшими партиями данных, объединив таблицу против самой себя, однако это очень неэффективное решение, и я знаю, что правильный способ достижения этого будет иметь какое-то преимущество под капотом. Запрос для этого выглядит следующим образом:

 WITH lhs AS (
    SELECT 
        ts, instrumentId, value, rank() OVER (ORDER BY ts)
    FROM prices
),
splay AS (
    SELECT
        lhs.instrumentId, lhs.ts, lhs.rank, rhs.value
    FROM lhs as rhs
    JOIN lhs
    ON 
        lhs.instrumentId=rhs.instrumentId
        AND rhs.ts BETWEEN lhs.ts - INTERVAL '5s' AND lhs.ts
    ORDER BY lhs.instrumentId, lhs.rank, rhs.rank
)
SELECT
    MAX(instrumentId), min(ts), AVG(value)
FROM splay
GROUP BY rank;
 

Вышесказанное занимает ~2 минуты для работы с данными в течение одного дня, но я могу выполнить операцию в pandas с данными за 1 год за ~45 секунд, поэтому я уверен, что в SQL есть лучший способ сделать это.

Как я могу добиться эффективных функций сгруппированных окон в timescaledb/postgres?

Ответ №1:

Я думаю, что вы ищете time_bucket группу рядом, так что что-то вроде:

 SELECT time_bucket('5s'::interval, ts), instrumentID, avg(value)
FROM prices
GROUP BY time_bucket('5s'::interval, ts), instrumentID;
 

Это сгруппируется по метке времени в начале диапазона, если вы хотите получить конец, вы можете просто добавить к нему «5s»…

Если вы пытаетесь сделать это для каждой строки в исходном наборе данных, то вам следует использовать оконную функцию с предложением partition by и предложением range следующим образом:

 SELECT *, avg(value) OVER (PARTITION BY instrumentId ORDER BY ts RANGE '5s' PRECEDING)
FROM prices; 
 

Комментарии:

1. Спасибо, но я не думаю, что это совсем то, что мне нужно. Это создает окна с центром в каждые 5 секунд (0, 5, 10 и т.д.). Я ищу окна с границей в каждой строке моей таблицы. Поэтому, если у меня есть измерения в 1, 2, 7, 8 секунд, я бы хотел 5-секундные окна с границей в каждой из этих точек.

2. Ах, ну, тогда вы просто используете функцию окна и просто разделяете по идентификатору инструмента, отредактированному, чтобы добавить это.

3. Обновленное предложение работает отлично, спасибо!