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

Jak získat průměrné hodnoty pro časové intervaly v Postgresu

Návrh DB

Zatímco můžete pracovat se samostatným date a time sloupců, skutečně neexistuje žádná výhoda oproti jedinému timestamp sloupec. Přizpůsobil bych se:

ALTER TABLE tbl ADD column ts timestamp;
UPDATE tbl SET ts = date + time;  -- assuming actual date and time types
ALTER TABLE tbl DROP column date, DROP column time;

Pokud datum a čas nejsou skutečné date a time datové typy, použijte to_timestamp() . Související:

Dotaz

Pak je dotaz o něco jednodušší:

SELECT *
FROM  (
   SELECT sn, generate_series(min(ts), max(ts), interval '5 min') AS ts
   FROM   tbl
   WHERE  sn = '4as11111111'
   AND    ts >= '2018-01-01'
   AND    ts <  '2018-01-02'
   GROUP  BY 1
   ) grid
CROSS  JOIN LATERAL (
   SELECT round(avg(vin1), 2) AS vin1_av
        , round(avg(vin2), 2) AS vin2_av
        , round(avg(vin3), 2) AS vin3_av
   FROM   tbl
   WHERE  sn =  grid.sn
   AND    ts >= grid.ts
   AND    ts <  grid.ts + interval '5 min'
   ) avg;

db<>fiddle zde

Vygenerujte mřížku časů zahájení v prvním dílčím dotazu grid , běží od první do poslední kvalifikace řádek v daném časovém rámci.

Připojte se k řádkům, které spadají do každého oddílu, pomocí LATERAL připojit a okamžitě agregovat průměry v poddotazu avg . Kvůli agregátům je to vždy vrátí řádek, i když nebyly nalezeny žádné položky. Výchozí průměry jsou NULL v tomto případě.

Výsledek zahrnuje všechny časové úseky mezi první a poslední kvalifikační řadou v daném časovém rámci. Smysl by dávaly i různé další výsledné kompozice. Jako včetně všech časové úseky v daném časovém rámci nebo pouze časové úseky se skutečnými hodnotami. Všechny možné, musel jsem vybrat jeden výklad.

Index

Mějte alespoň tento vícesloupcový index:

CRATE INDEX foo_idx ON tbl (sn, ts);

Nebo na (sn, ts, vin1, vin2, vin3) umožnit skenování pouze na základě indexu – pokud jsou splněny některé předpoklady a zejména pokud jsou řádky tabulky mnohem širší než v ukázce.

Úzce související:

Na základě vaší původní tabulky

Jak bylo požadováno a vysvětleno v komentáři a později znovu aktualizován v otázce, aby zahrnoval sloupce mac a loc . Předpokládám, že chcete samostatné průměry pro (mac, loc) .

date a time jsou stále samostatné sloupce, sloupce vin* jsou typu float a vyloučit časové úseky bez řádků:

Aktualizovaný dotaz také přesune funkci vracející sadu generate_series() do FROM seznam, který je před Postgres 10 čistší:

SELECT t.mac, sn.sn, t.loc, ts.ts::time AS time, ts.ts::date AS date
     , t.vin1_av, t.vin2_av, t.vin3_av
FROM  (SELECT text '4as11111111') sn(sn)  -- provide sn here once
CROSS  JOIN LATERAL (
   SELECT min(date+time) AS min_ts, max(date+time) AS max_ts
   FROM   tbl
   WHERE  sn = sn.sn
   AND    date+time >= '2018-01-01 0:0'   -- provide time frame here
   AND    date+time <  '2018-01-02 0:0'
   ) grid
CROSS  JOIN LATERAL generate_series(min_ts, max_ts, interval '5 min') ts(ts)
CROSS  JOIN LATERAL (
   SELECT mac, loc
        , round(avg(vin1)::numeric, 2) AS vin1_av  -- cast to numeric for round()
        , round(avg(vin2)::numeric, 2) AS vin2_av  -- but rounding is optional
        , round(avg(vin3)::numeric, 2) AS vin3_av
   FROM   tbl
   WHERE  sn = sn.sn
   AND    date+time >= ts.ts
   AND    date+time <  ts.ts + interval '5 min'
   GROUP  BY mac, loc
   HAVING count(*) > 0  -- exclude empty slots
   ) t;

Vytvořte vícesloupcový index výrazu, který toto podporuje:

CRATE INDEX bar_idx ON tbl (sn, (date+time));

db<>fiddle zde

Ale mnohem raději bych použil timestamp po celou dobu.




  1. PHP/MySQL - jak automaticky vygenerovat odkaz na odstranění řádku v již automaticky vygenerované tabulce?

  2. Chyba vlastnosti ConnectionString ve VB.NET nebyla inicializována

  3. JSON_STORAGE_SIZE() – Najděte velikost úložiště dokumentu JSON v MySQL

  4. SQL, vnitřní spojení, nahrazení id názvem