sql >> Databáze >  >> RDS >> PostgreSQL

PostgreSQL:běžící počet řádků pro dotaz „po minutě“

Vraťte pouze minuty s aktivitou

Nejkratší

SELECT DISTINCT
       date_trunc('minute', "when") AS minute
     , count(*) OVER (ORDER BY date_trunc('minute', "when")) AS running_ct
FROM   mytable
ORDER  BY 1;

Použijte date_trunc() , vrátí přesně to, co potřebujete.

Nezahrnujte id v dotazu, protože chcete GROUP BY minutové řezy.

count() se obvykle používá jako jednoduchá agregační funkce. Připojení OVER klauzule z něj dělá funkci okna. Vynechat PARTITION BY v definici okna - chcete průběžný počet přes všechny řádky . Ve výchozím nastavení se to počítá od prvního řádku po posledního partnera aktuálního řádku, jak je definováno v ORDER BY . Manuál:

Výchozí možnost orámování je RANGE UNBOUNDED PRECEDING , což je totéž jako RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW . Pomocí ORDER BY ,toto nastaví rámec tak, aby byly všechny řádky od začátku oddílu až po poslední ORDER BY aktuálního řádku peer.

A to je náhodou přesně co potřebujete.

Použijte count(*) spíše než count(id) . Lépe to odpovídá vaší otázce („počet řádků“). Obecně je o něco rychlejší než count(id) . A i když bychom mohli předpokládat, že id je NOT NULL , nebyl v otázce specifikován, takže count(id) je nesprávný , přísně vzato, protože hodnoty NULL se nepočítají s count(id) .

Nemůžete GROUP BY minutové řezy na stejné úrovni dotazu. Agregační funkce jsou použity před okenní funkce, funkce okna count(*) by tímto způsobem viděl pouze 1 řádek za minutu.
Můžete však SELECT DISTINCT , protože DISTINCT se použije po funkce okna.

ORDER BY 1 je jen zkratka pro ORDER BY date_trunc('minute', "when") zde.
1 je poziční odkaz na 1. výraz v SELECT seznam.

Použijte to_char() pokud potřebujete formátovat výsledek. Jako:

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");

Nejrychlejší

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;

Podobně jako výše, ale:

K agregaci a počítání řádků za minutu používám poddotaz. Tímto způsobem získáme 1 řádek za minutu bez DISTINCT ve vnějším SELECT .

Použijte sum() jako funkci agregace oken nyní k sečtení počtů z dílčího dotazu.

Zjistil jsem, že je to podstatně rychlejší s mnoha řádky za minutu.

Zahrnout minuty bez aktivity

Nejkratší

@GabiMe se v komentáři zeptala, jak získat jeden řádek za každý minute v časovém rámci, včetně těch, kde nenastala žádná událost (žádný řádek v základní tabulce):

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;

Vygenerujte řádek pro každou minutu v časovém rámci mezi první a poslední událostí pomocí generate_series() - zde přímo na základě agregovaných hodnot z dílčího dotazu.

LEFT JOIN ke všem časovým razítkům zkráceným na minutu a počítat. NULL hodnoty (kde neexistuje žádný řádek) se nepřidávají k průběžnému počtu.

Nejrychlejší

S 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;

Opět v prvním kroku agregujte a počítejte řádky za minutu, odpadá nutnost pozdějšího DISTINCT .

Liší se od count() , sum() může vrátit NULL . Výchozí hodnota je 0 s COALESCE .

S mnoha řádky a indexem na "when" tato verze s poddotazem byla nejrychlejší mezi několika variantami, které jsem testoval s 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. Jak zjistit, že transakce již byla zahájena?

  2. Porovnání výkonu virtuálních počítačů Windows Azure, část 2

  3. Jak nainstalovat databázi MariaDB v Debianu 10

  4. java.lang.NoSuchFieldError:NONE v hibernaci s Spring 3, maven, JPA, c3p0