За щастие използвате PostgreSQL. Прозоречната функция generate_series()
е твой приятел.
Тестов случай
Като се има предвид следната тестова таблица (която вие трябваше да предостави):
CREATE TABLE event(event_id serial, ts timestamp);
INSERT INTO event (ts)
SELECT generate_series(timestamp '2018-05-01'
, timestamp '2018-05-08'
, interval '7 min') + random() * interval '7 min';
Едно събитие на всеки 7 минути (плюс 0 до 7 минути, произволно).
Основно решение
Тази заявка отчита събития за произволен интервал от време. 17 минути в примера:
WITH grid AS (
SELECT start_time
, lead(start_time, 1, 'infinity') OVER (ORDER BY start_time) AS end_time
FROM (
SELECT generate_series(min(ts), max(ts), interval '17 min') AS start_time
FROM event
) sub
)
SELECT start_time, count(e.ts) AS events
FROM grid g
LEFT JOIN event e ON e.ts >= g.start_time
AND e.ts < g.end_time
GROUP BY start_time
ORDER BY start_time;
-
Заявката извлича минимални и максимални
ts
от основната таблица, за да покрие целия времеви диапазон. Вместо това можете да използвате произволен времеви диапазон. -
Предоставетевсяко времеви интервал според нуждите.
-
Създава един ред за всеки времеви слот. Ако не се случи събитие през този интервал, броят е
0
. -
Не забравяйте да се справите с горната и долната граница правилно:
- Неочаквани резултати от SQL заявка с МЕЖДУ времеви марки
-
Прозоречната функция
lead()
има често пренебрегвана функция:може да предостави по подразбиране, когато не съществува водещ ред. Предоставяне на'infinity'
в примера. В противен случай последният интервал ще бъде отрязан с горна границаNULL
.
Минимален еквивалент
Горната заявка използва CTE и lead()
и многословен синтаксис. Елегантно и може би по-лесно за разбиране, но малко по-скъпо. Ето по-кратка, по-бърза, минимална версия:
SELECT start_time, count(e.ts) AS events
FROM (SELECT generate_series(min(ts), max(ts), interval '17 min') FROM event) g(start_time)
LEFT JOIN event e ON e.ts >= g.start_time
AND e.ts < g.start_time + interval '17 min'
GROUP BY 1
ORDER BY 1;
Пример за "на всеки 15 минути през последната седмица"`
И форматиране с to_char()
.
SELECT to_char(start_time, 'YYYY-MM-DD HH24:MI'), count(e.ts) AS events
FROM generate_series(date_trunc('day', localtimestamp - interval '7 days')
, localtimestamp
, interval '15 min') g(start_time)
LEFT JOIN event e ON e.ts >= g.start_time
AND e.ts < g.start_time + interval '15 min'
GROUP BY start_time
ORDER BY start_time;
Все още ORDER BY
и GROUP BY
върху основната стойност на времевата марка , а не на форматирания низ. Това е по-бързо и по-надеждно.
db<>цигулка тук
Свързан отговор, генериращ текущ брой във времевия период:
- PostgreSQL:текущ брой редове за заявка „по минута“