Genfør kun minutter med aktivitet
Korteste
SELECT DISTINCT
date_trunc('minute', "when") AS minute
, count(*) OVER (ORDER BY date_trunc('minute', "when")) AS running_ct
FROM mytable
ORDER BY 1;
Brug date_trunc()
, det returnerer præcis det, du har brug for.
Medtag ikke id
i forespørgslen, da du vil GROUP BY
minutskiver.
count()
bruges typisk som almindelig aggregeret funktion. Tilføjelse af en OVER
klausul gør det til en vinduesfunktion. Udelad PARTITION BY
i vinduesdefinitionen - du vil have en løbende optælling over alle rækker . Som standard tæller det fra den første række til den sidste peer i den aktuelle række som defineret af ORDER BY
. Manualen:
Standardindstillingsindstillingen er RANGE UNBOUNDED PRECEDING
, hvilket er det samme som RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
. Med ORDER BY
, dette indstiller rammen til at være alle rækker fra partitionens start op til den aktuelle rækkes sidste ORDER BY
peer.
Og det er tilfældigvis præcis hvad du har brug for.
Brug count(*)
i stedet for count(id)
. Det passer bedre til dit spørgsmål ("antal rækker"). Det er generelt lidt hurtigere end count(id)
. Og mens vi måske antager, at id
er NOT NULL
, det er ikke angivet i spørgsmålet, så count(id)
er forkert , strengt taget, fordi NULL-værdier ikke tælles med count(id)
.
Du kan ikke GROUP BY
minutskiver på samme forespørgselsniveau. Samlede funktioner anvendes før vinduesfunktioner, vinduesfunktionen count(*)
ville kun se 1 række i minuttet på denne måde.
Du kan dog SELECT DISTINCT
, fordi DISTINCT
anvendes efter vinduesfunktioner.
ORDER BY 1
er kun en forkortelse for ORDER BY date_trunc('minute', "when")
her.1
er en positionsreference til det 1. udtryk i SELECT
liste.
Brug to_char()
hvis du skal formatere resultatet. Ligesom:
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");
Hurtigste
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;
Meget ligesom ovenstående, men:
Jeg bruger en underforespørgsel til at samle og tælle rækker i minuttet. På denne måde får vi 1 række i minuttet uden DISTINCT
i den ydre SELECT
.
Brug sum()
som vinduesaggregeringsfunktion nu for at tilføje tællingerne fra underforespørgslen.
Jeg fandt, at dette var væsentligt hurtigere med mange rækker i minuttet.
Inkluder minutter uden aktivitet
Korteste
@GabiMe spurgte i en kommentar, hvordan man får en række for hver minute
i tidsrammen, inklusive dem, hvor ingen hændelse fandt sted (ingen række i basistabellen):
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;
Generer en række for hvert minut i tidsrammen mellem den første og den sidste hændelse med generate_series()
- her direkte baseret på aggregerede værdier fra underforespørgslen.
LEFT JOIN
til alle tidsstempler afkortet til minut og tæller. NULL
værdier (hvor der ikke findes en række) føjes ikke til det løbende antal.
Hurtigste
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;
Igen, aggreger og tæl rækker per minut i det første trin, det udelader behovet for senere DISTINCT
.
Forskellig fra count()
, sum()
kan returnere NULL
. Standard er 0
med COALESCE
.
Med mange rækker og et indeks på "when"
denne version med en underforespørgsel var hurtigst blandt et par varianter, jeg testede 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;