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

Nejlepší způsob, jak počítat záznamy podle libovolných časových intervalů v Rails+Postgres

Naštěstí používáte PostgreSQL. Funkce okna generate_series() je tvůj přítel.

Testovací případ

Vzhledem k následující testovací tabulce (kterou vy měl poskytnout):

CREATE TABLE event(event_id serial, ts timestamp);
INSERT INTO event (ts)
SELECT generate_series(timestamp '2018-05-01'
                     , timestamp '2018-05-08'
                     , interval '7 min') + random() * interval '7 min';

Jedna událost každých 7 minut (plus 0 až 7 minut, náhodně).

Základní řešení

Tento dotaz počítá události pro libovolný časový interval. 17 minut v příkladu:

WITH grid AS (
   SELECT start_time
        , lead(start_time, 1, 'infinity') OVER (ORDER BY start_time) AS end_time
   FROM  (
      SELECT generate_series(min(ts), max(ts), interval '17 min') AS start_time
      FROM   event
      ) sub
   )
SELECT start_time, count(e.ts) AS events
FROM   grid       g
LEFT   JOIN event e ON e.ts >= g.start_time
                   AND e.ts <  g.end_time
GROUP  BY start_time
ORDER  BY start_time;
  • Dotaz načte minimální a maximální ts ze základní tabulky, aby bylo pokryto celé časové rozmezí. Místo toho můžete použít libovolný časový rozsah.

  • Poskytněte jakékoli časový interval podle potřeby.

  • Vytvoří jeden řádek pro každý Chvilka. Pokud během tohoto intervalu nedošlo k žádné události, je počet 0 .

  • Ujistěte se, že zacházíte s horní a dolní hranicí správně:

    • Neočekávané výsledky z SQL dotazu s BETWEEN časovými razítky
  • Funkce okna lead() má často přehlíženou funkci:může poskytnout výchozí hodnotu pro případ, kdy neexistuje žádný úvodní řádek. Poskytování 'infinity' v příkladu. V opačném případě by byl poslední interval oříznut horní hranicí NULL .

Minimální ekvivalent

Výše uvedený dotaz používá CTE a lead() a podrobnou syntaxi. Elegantní a možná srozumitelnější, ale o něco dražší. Zde je kratší, rychlejší, minimální verze:

SELECT start_time, count(e.ts) AS events
FROM  (SELECT generate_series(min(ts), max(ts), interval '17 min') FROM event) g(start_time)
LEFT   JOIN event e ON e.ts >= g.start_time
                   AND e.ts <  g.start_time + interval '17 min'
GROUP  BY 1
ORDER  BY 1;

Příklad pro „každých 15 minut za poslední týden“`

A formátování pomocí to_char() .

SELECT to_char(start_time, 'YYYY-MM-DD HH24:MI'), count(e.ts) AS events
FROM   generate_series(date_trunc('day', localtimestamp - interval '7 days')
                     , localtimestamp
                     , interval '15 min') g(start_time)
LEFT   JOIN event e ON e.ts >= g.start_time
                   AND e.ts <  g.start_time + interval '15 min'
GROUP  BY start_time
ORDER  BY start_time;

Stále ORDER BY a GROUP BY na hodnotě podkladového časového razítka , nikoli na formátovaný řetězec. Je to rychlejší a spolehlivější.

db<>zde hrajte

Související odpověď vytvářející průběžný počet v časovém rámci:

  • PostgreSQL:průběžný počet řádků pro dotaz „po minutách“



  1. Jaký je rozdíl mezi MySQL, MySQLi a PDO?

  2. Při výběru a zakódování základního 64 obrázku z databáze byla zjištěna pomalost

  3. Funkce SQLite Like() s příklady

  4. Zkopírujte několik sloupců souboru csv do tabulky