sql >> Database >  >> RDS >> PostgreSQL

PostgreSQL:lopende telling van rijen voor een zoekopdracht 'per minuut'

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 als RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW . Met ORDER BY , dit stelt het frame in op alle rijen vanaf de partitie die begint tot en met de laatste ORDER 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;



  1. Is er ANY_VALUE mogelijkheid voor mysql 5.6?

  2. hoe gebruik je check constraint in oracle?

  3. Tips voor het opslaan van PostgreSQL-back-ups op Google Cloud (GCP)

  4. Praag PostgreSQL Developer Day 2016