DB-design
Mens du kan arbejde med separat dato
og tid
kolonner, er der virkelig ingen fordel i forhold til et enkelt tidsstempel
kolonne. Jeg ville tilpasse:
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;
Hvis dato og klokkeslæt ikke er faktisk dato
og tid
datatyper, brug to_timestamp()
. Relateret:
Forespørgsel
Så er forespørgslen lidt enklere:
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 her
Generer et gitter af starttider i den første underforespørgsel gitter
, der løber fra den første til den sidste kvalificerende række i den givne tidsramme.
Slut dig til rækker, der falder i hver partition med en LATERAL
join og aggreger straks gennemsnit i underforespørgslen avg
. På grund af aggregaterne er det altid returnerer en række, selvom der ikke findes nogen poster. Gennemsnit er som standard NULL
i dette tilfælde.
Resultatet inkluderer alle tidsintervaller mellem første og sidste kvalificerende række i den givne tidsramme. Forskellige andre resultatsammensætninger ville også give mening. Gerne inklusive alle timeslots i den givne tidsramme eller blot tidsslots med faktiske værdier. Alt muligt, jeg var nødt til at vælge én fortolkning.
Indeks
Hav i det mindste dette flerkolonneindeks:
CRATE INDEX foo_idx ON tbl (sn, ts);
Eller på (sn, ts, vin1, vin2, vin3)
at tillade kun indeksscanninger - hvis nogle forudsætninger er opfyldt, og især hvis tabelrækkerne er meget bredere end i demoen.
Nært beslægtet:
- Langsom LEFT JOIN på CTE med tidsintervaller
- Bedste måde at tælle poster efter vilkårlige tidsintervaller i Rails+Postgres
Baseret på din oprindelige tabel
Som anmodet og præciseret i kommentaren
, og senere opdateret igen i spørgsmålet til at inkludere kolonnerne mac
og loc
. Jeg antager, at du vil have separate gennemsnit pr. (mac, loc)
.
dato
og tid
er stadig separate kolonner, vin* kolonner er typen float
, og ekskluder tidsintervaller uden rækker:
Den opdaterede forespørgsel flytter også sæt-retur-funktionen generate_series()
til FROM
liste, som er renere før 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;
Opret et flerkolonne udtryksindeks for at understøtte dette:
CRATE INDEX bar_idx ON tbl (sn, (date+time));
db<>fiddle her
Men jeg vil meget hellere bruge timestamp
hele tiden.