Na szczęście używasz PostgreSQL. Funkcja okna generate_series()
jest twoim przyjacielem.
Przypadek testowy
Biorąc pod uwagę następującą tabelę testową (którą ty powinien był podać):
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';
Jedno wydarzenie na każde 7 minut (plus od 0 do 7 minut, losowo).
Podstawowe rozwiązanie
To zapytanie zlicza zdarzenia w dowolnym przedziale czasu. 17 minut w przykładzie:
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;
-
Zapytanie pobiera minimalny i maksymalny
ts
z tabeli podstawowej, aby objąć cały zakres czasu. Zamiast tego możesz użyć dowolnego zakresu czasu. -
Podaj dowolne przedział czasu w razie potrzeby.
-
Tworzy jeden wiersz dla każdego przedział czasu. Jeśli w tym okresie nie wydarzyło się żadne zdarzenie, liczba wynosi
0
. -
Pamiętaj, aby poradzić sobie z górną i dolną granicą poprawnie:
- Nieoczekiwane wyniki zapytania SQL ze znacznikami czasu BETWEEN
-
Funkcja okna
lead()
ma często pomijaną funkcję:może zapewnić wartość domyślną, gdy nie istnieje żaden wiersz wiodący. Zapewnienie'infinity'
w przykładzie. W przeciwnym razie ostatni interwał zostałby odcięty górną granicąNULL
.
Minimalny odpowiednik
Powyższe zapytanie używa CTE i lead()
i pełna składnia. Elegancki i może łatwiejszy do zrozumienia, ale nieco droższy. Oto krótsza, szybsza i minimalna wersja:
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;
Przykład „co 15 minut w ostatnim tygodniu”
I formatowanie za pomocą 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;
Nadal ORDER BY
i GROUP BY
na podstawie sygnatury czasowej wartość , a nie na sformatowanym ciągu. To jest szybsze i bardziej niezawodne.
db<>graj tutaj
Powiązana odpowiedź dająca działającą liczbę w ramach czasowych:
- PostgreSQL:liczenie wierszy dla zapytania „po minucie”