Retour slechts minuten met activiteit
Kortste
SELECT DISTINCT
date_trunc('minute', "when") AS minute
, count(*) OVER (ORDER BY date_trunc('minute', "when")) AS running_ct
FROM mytable
ORDER BY 1;
Gebruik date_trunc()
, het geeft precies terug wat je nodig hebt.
Gebruik geen id
in de zoekopdracht, omdat je wilt GROUP BY
minuten plakjes.
count()
wordt meestal gebruikt als een gewone aggregaatfunctie. Een OVER
toevoegen clausule maakt er een vensterfunctie van. Weglaten PARTITION BY
in de vensterdefinitie - u wilt een lopende telling over alle rijen . Standaard telt dat van de eerste rij tot de laatste peer van de huidige rij zoals gedefinieerd door ORDER BY
. De handleiding:
De standaard framing-optie is
RANGE UNBOUNDED PRECEDING
, wat hetzelfde is alsRANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
. MetORDER BY
, dit stelt het frame in op alle rijen vanaf de partitie die begint tot en met de laatsteORDER BY
van de huidige rij peer.
En dat is precies wat je nodig hebt.
Gebruik count(*)
in plaats van count(id)
. Het past beter bij uw vraag ("aantal rijen"). Het is over het algemeen iets sneller dan count(id)
. En hoewel we mogen aannemen dat id
is NOT NULL
, het is niet gespecificeerd in de vraag, dus count(id)
is fout , strikt genomen, omdat NULL-waarden niet worden geteld met count(id)
.
U kunt niet GROUP BY
minuutsegmenten op hetzelfde zoekniveau. Geaggregeerde functies worden voor applied toegepast vensterfuncties, de vensterfunctie count(*)
zou op deze manier maar 1 rij per minuut zien.
U kunt echter SELECT DISTINCT
, omdat DISTINCT
wordt toegepast na vensterfuncties.
ORDER BY 1
is gewoon een afkorting voor ORDER BY date_trunc('minute', "when")
hier.1
is een positionele verwijzing naar de eerste uitdrukking in de SELECT
lijst.
Gebruik to_char()
als u het resultaat moet opmaken. Vind ik leuk:
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");
Snelst
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;
Net zoals het bovenstaande, maar:
Ik gebruik een subquery om rijen per minuut te aggregeren en te tellen. Op deze manier krijgen we 1 rij per minuut zonder DISTINCT
in de buitenste SELECT
.
Gebruik sum()
als vensteraggregatiefunctie nu om de tellingen van de subquery op te tellen.
Ik vond dit aanzienlijk sneller met veel rijen per minuut.
Voeg minuten toe zonder activiteit
Kortste
@GabiMe vroeg in een reactie hoe je een rij kunt krijgen voor elke minute
in het tijdsbestek, inclusief die waarin zich geen gebeurtenis heeft voorgedaan (geen rij in de basistabel):
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;
Genereer een rij voor elke minuut in het tijdsbestek tussen de eerste en de laatste gebeurtenis met generate_series()
- hier direct gebaseerd op geaggregeerde waarden uit de subquery.
LEFT JOIN
naar alle tijdstempels afgekapt tot op de minuut en tellen. NULL
waarden (waar geen rij bestaat) tellen niet op bij de lopende telling.
Snelst
Met 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;
Nogmaals, aggregeer en tel rijen per minuut in de eerste stap, het laat de noodzaak voor latere DISTINCT
weg. .
Anders dan count()
, sum()
kan NULL
teruggeven . Standaard op 0
met COALESCE
.
Met veel rijen en een index op "when"
deze versie met een subquery was het snelst van een aantal varianten die ik heb getest met 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;