Returnera bara minuter med aktivitet
Kortast
SELECT DISTINCT
date_trunc('minute', "when") AS minute
, count(*) OVER (ORDER BY date_trunc('minute', "when")) AS running_ct
FROM mytable
ORDER BY 1;
Använd date_trunc()
, den returnerar precis det du behöver.
Inkludera inte id
i frågan, eftersom du vill GROUP BY
minutskivor.
count()
används vanligtvis som vanlig aggregatfunktion. Lägger till en OVER
klausul gör det till en fönsterfunktion. Utelämna PARTITION BY
i fönsterdefinitionen - du vill ha en löpande räkning över alla rader . Som standard räknas det från den första raden till den sista peeren i den aktuella raden enligt definitionen av ORDER BY
. Manualen:
Standardinramningsalternativet är
RANGE UNBOUNDED PRECEDING
, vilket är samma somRANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
. MedORDER BY
, detta ställer in ramen för att vara alla rader från partitionsstarten upp till den aktuella radens senasteORDER BY
peer.
Och det råkar vara exakt vad du behöver.
Använd count(*)
istället för count(id)
. Det passar bättre på din fråga ("antal rader"). Det är i allmänhet något snabbare än count(id)
. Och även om vi kan anta att id
är NOT NULL
, det har inte specificerats i frågan, så count(id)
är fel , strängt taget, eftersom NULL-värden inte räknas med count(id)
.
Du kan inte GROUP BY
minutskivor på samma frågenivå. Aggregatfunktioner tillämpas före fönsterfunktioner, fönsterfunktionen count(*)
skulle bara se 1 rad per minut på detta sätt.
Du kan dock SELECT DISTINCT
, eftersom DISTINCT
tillämpas efter fönsterfunktioner.
ORDER BY 1
är bara en förkortning för ORDER BY date_trunc('minute', "when")
här.1
är en positionsreferens till det första uttrycket i SELECT
lista.
Använd to_char()
om du behöver formatera resultatet. Gilla:
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");
Snabbast
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;
Ungefär som ovan, men:
Jag använder en underfråga för att aggregera och räkna rader per minut. På så sätt får vi 1 rad per minut utan DISTINCT
i den yttre SELECT
.
Använd sum()
som fönsteraggregatfunktion nu för att lägga till antalet från underfrågan.
Jag tyckte att detta var betydligt snabbare med många rader per minut.
Inkludera minuter utan aktivitet
Kortast
@GabiMe frågade i en kommentar hur man får en rad för varje minute
i tidsramen, inklusive de där ingen händelse inträffade (ingen rad i bastabellen):
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;
Generera en rad för varje minut i tidsramen mellan den första och den sista händelsen med generate_series()
- här direkt baserat på aggregerade värden från underfrågan.
LEFT JOIN
till alla tidsstämplar trunkerade till minut och räkning. NULL
värden (där ingen rad finns) läggs inte till det löpande antalet.
Snabbast
Med 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;
Återigen, aggregera och räkna rader per minut i det första steget, det utesluter behovet av senare DISTINCT
.
Skiljer sig från count()
, sum()
kan returnera NULL
. Standard är 0
med COALESCE
.
Med många rader och ett index på "when"
den här versionen med en underfråga var snabbast bland ett par varianter jag testade med 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;