Restituisci solo minuti con attività
Il più corto
SELECT DISTINCT
date_trunc('minute', "when") AS minute
, count(*) OVER (ORDER BY date_trunc('minute', "when")) AS running_ct
FROM mytable
ORDER BY 1;
Usa date_trunc() , restituisce esattamente ciò di cui hai bisogno.
Non includere id nella query, poiché vuoi GROUP BY fette minute.
count() è tipicamente usato come semplice funzione aggregata. Aggiunta di un OVER clausola lo rende una funzione di finestra. Ometti PARTITION BY nella definizione della finestra - vuoi un conteggio continuo su tutte le righe . Per impostazione predefinita, conta dalla prima riga all'ultimo peer della riga corrente come definito da ORDER BY . Il manuale:
L'opzione di inquadratura predefinita è RANGE UNBOUNDED PRECEDING , che è lo stesso di RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW . Con ORDER BY , questo imposta il frame in modo che sia tutte le righe dall'inizio della partizione fino all'ultimo ORDER BY della riga corrente pari.
E questo è esattamente quello che ti serve.
Usa count(*) anziché count(id) . Si adatta meglio alla tua domanda ("conteggio di righe"). In genere è leggermente più veloce di count(id) . E, mentre potremmo supporre che id è NOT NULL , non è stato specificato nella domanda, quindi count(id) è sbagliato , in senso stretto, perché i valori NULL non vengono conteggiati con count(id) .
Non puoi GROUP BY porzioni di minuti allo stesso livello di query. Le funzioni aggregate vengono applicate prima funzioni della finestra, la funzione della finestra count(*) vedrebbe solo 1 riga al minuto in questo modo.
Puoi, tuttavia, SELECT DISTINCT , perché DISTINCT viene applicato dopo funzioni della finestra.
ORDER BY 1 è solo un'abbreviazione per ORDER BY date_trunc('minute', "when") qui.1 è un riferimento di riferimento posizionale alla prima espressione in SELECT elenco.
Usa to_char() se è necessario formattare il risultato. Come:
SELECT DISTINCT
to_char(date_trunc('minute', "when"), 'DD.MM.YYYY HH24:MI') AS minute
, count(*) OVER (ORDER BY date_trunc('minute', "when")) AS running_ct
FROM mytable
ORDER BY date_trunc('minute', "when");
Il più veloce
SELECT minute, sum(minute_ct) OVER (ORDER BY minute) AS running_ct
FROM (
SELECT date_trunc('minute', "when") AS minute
, count(*) AS minute_ct
FROM tbl
GROUP BY 1
) sub
ORDER BY 1;
Molto simile a quanto sopra, ma:
Uso una sottoquery per aggregare e contare le righe al minuto. In questo modo otteniamo 1 riga al minuto senza DISTINCT nel SELECT esterno .
Usa sum() come funzione di aggregazione della finestra ora per sommare i conteggi dalla sottoquery.
Ho scoperto che questo è sostanzialmente più veloce con molte righe al minuto.
Includi minuti senza attività
Il più corto
@GabiMe ha chiesto in un commento come ottenere una riga per ogni minute nell'intervallo di tempo, compresi quelli in cui non si è verificato alcun evento (nessuna riga nella tabella di base):
SELECT DISTINCT
minute, count(c.minute) OVER (ORDER BY minute) AS running_ct
FROM (
SELECT generate_series(date_trunc('minute', min("when"))
, max("when")
, interval '1 min')
FROM tbl
) m(minute)
LEFT JOIN (SELECT date_trunc('minute', "when") FROM tbl) c(minute) USING (minute)
ORDER BY 1;
Genera una riga per ogni minuto nell'intervallo di tempo compreso tra il primo e l'ultimo evento con generate_series() - qui direttamente in base ai valori aggregati della sottoquery.
LEFT JOIN a tutti i timestamp troncati al minuto e contare. NULL i valori (dove non esiste alcuna riga) non vengono aggiunti al conteggio corrente.
Il più veloce
Con CTE:
WITH cte AS (
SELECT date_trunc('minute', "when") AS minute, count(*) AS minute_ct
FROM tbl
GROUP BY 1
)
SELECT m.minute
, COALESCE(sum(cte.minute_ct) OVER (ORDER BY m.minute), 0) AS running_ct
FROM (
SELECT generate_series(min(minute), max(minute), interval '1 min')
FROM cte
) m(minute)
LEFT JOIN cte USING (minute)
ORDER BY 1;
Ancora una volta, aggrega e conta le righe al minuto nel primo passaggio, omette la necessità di un successivo DISTINCT .
Diverso da count() , sum() può restituire NULL . Il valore predefinito è 0 con COALESCE .
Con molte righe e un indice su "when" questa versione con una sottoquery è stata la più veloce tra un paio di varianti che ho testato con Postgres 9.1 - 9.4:
SELECT m.minute
, COALESCE(sum(c.minute_ct) OVER (ORDER BY m.minute), 0) AS running_ct
FROM (
SELECT generate_series(date_trunc('minute', min("when"))
, max("when")
, interval '1 min')
FROM tbl
) m(minute)
LEFT JOIN (
SELECT date_trunc('minute', "when") AS minute
, count(*) AS minute_ct
FROM tbl
GROUP BY 1
) c USING (minute)
ORDER BY 1;