sql >> Databasteknik >  >> RDS >> PostgreSQL

PostgreSQL:kör antal rader för en fråga "per minut"

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 som RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW . Med ORDER BY , detta ställer in ramen för att vara alla rader från partitionsstarten upp till den aktuella radens senaste ORDER 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;



  1. SQL Server:Indexkolumner som används i liknande?

  2. Vanligt tabelluttryck, varför semikolon?

  3. Hur gör man:Rengör en mysql InnoDB-lagringsmotor?

  4. MyBatis Batch Insert/Update för Oracle