Naštěstí používáte PostgreSQL. Funkce okna generate_series()
je tvůj přítel.
Testovací případ
Vzhledem k následující testovací tabulce (kterou vy měl poskytnout):
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';
Jedna událost každých 7 minut (plus 0 až 7 minut, náhodně).
Základní řešení
Tento dotaz počítá události pro libovolný časový interval. 17 minut v příkladu:
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;
-
Dotaz načte minimální a maximální
ts
ze základní tabulky, aby bylo pokryto celé časové rozmezí. Místo toho můžete použít libovolný časový rozsah. -
Poskytněte jakékoli časový interval podle potřeby.
-
Vytvoří jeden řádek pro každý Chvilka. Pokud během tohoto intervalu nedošlo k žádné události, je počet
0
. -
Ujistěte se, že zacházíte s horní a dolní hranicí správně:
- Neočekávané výsledky z SQL dotazu s BETWEEN časovými razítky
-
Funkce okna
lead()
má často přehlíženou funkci:může poskytnout výchozí hodnotu pro případ, kdy neexistuje žádný úvodní řádek. Poskytování'infinity'
v příkladu. V opačném případě by byl poslední interval oříznut horní hranicíNULL
.
Minimální ekvivalent
Výše uvedený dotaz používá CTE a lead()
a podrobnou syntaxi. Elegantní a možná srozumitelnější, ale o něco dražší. Zde je kratší, rychlejší, minimální verze:
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;
Příklad pro „každých 15 minut za poslední týden“`
A formátování 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;
Stále ORDER BY
a GROUP BY
na hodnotě podkladového časového razítka , nikoli na formátovaný řetězec. Je to rychlejší a spolehlivější.
db<>zde hrajte
Související odpověď vytvářející průběžný počet v časovém rámci:
- PostgreSQL:průběžný počet řádků pro dotaz „po minutách“