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:
- Långsam LEFT JOIN på CTE med tidsintervall
- Bästa sättet att räkna poster efter godtyckliga tidsintervall i Rails+Postgres
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.