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;