sql >> Database teknologi >  >> RDS >> PostgreSQL

PostgreSQL:kørende antal rækker for en forespørgsel 'efter minut'

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;



  1. MySQL returnerer kun én række

  2. Hvordan analyserer man strenge som en professionel ved hjælp af SQL SUBSTRING()-funktionen?

  3. MariaDB introducerer TO_CHAR()

  4. Sådan undgår du sløjfetriggeropkald i PostgreSQL 9.2.1