sql >> Databasteknik >  >> RDS >> PostgreSQL

Hur man får medelvärden för tidsintervall i Postgres

DB-design

Medan du kan arbeta med separat datum och tid kolumner, finns det egentligen ingen fördel jämfört med en enda tidsstämpel kolumn. Jag skulle anpassa:

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;

Om datum och tid inte är faktiska datum och tid datatyper, använd to_timestamp() . Relaterat:

Fråga

Då är frågan lite enklare:

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<>fiol här

Generera ett rutnät med starttider i den första underfrågan grid , från den första till den sista kvalificeringen rad inom den angivna tidsramen.

Gå med i rader som faller i varje partition med en SIDA gå med och omedelbart aggregera medelvärden i underfrågan avg . På grund av aggregaten är det alltid returnerar en rad även om inga poster hittas. Medelvärden är som standard NULL i det här fallet.

Resultatet inkluderar alla tidsluckor mellan första och sista kvalificerande raden i den givna tidsramen. Olika andra resultatkompositioner skulle också vara vettiga. Gilla att inkludera alla tidluckor i den givna tidsramen eller bara tidsluckor med faktiska värden. Allt möjligt, jag var tvungen att välja en tolkning.

Index

Ha åtminstone detta flerkolumnindex:

CRATE INDEX foo_idx ON tbl (sn, ts);

Eller på (sn, ts, vin1, vin2, vin3) för att tillåta genomsökningar endast av index - om vissa förutsättningar är uppfyllda och särskilt om tabellraderna är mycket bredare än i demon.

Närbesläktade:

Baserat på din ursprungliga tabell

Som begärt och förtydligat i kommentaren , och senare uppdateras igen i frågan för att inkludera kolumnerna mac och loc . Jag antar att du vill ha separata medelvärden per (mac, loc) .

datum och tid är fortfarande separata kolumner, vin* kolumner är typ float , och exkludera tidsluckor utan rader:

Den uppdaterade frågan flyttar också set-retur-funktionen generate_series() till FRÅN lista, som är renare före Postgres 10:

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;

Skapa ett uttrycksindex med flera kolumner för att stödja detta:

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

db<>fiol här

Men jag skulle mycket hellre använda tidsstämpel hela tiden.




  1. uppdatera användarlistan i gruppen:UPPDATERA eller DELETE + INSERT

  2. Vila gå med två tabeller och hämta alla poster?

  3. ODAC verkar cacha tabellschema?

  4. Valfria kolumner för att filtrera databasposter