sql >> Database >  >> RDS >> PostgreSQL

Hoe u gemiddelde waarden voor tijdsintervallen in Postgres krijgt

DB-ontwerp

Terwijl je kunt werk met aparte date en time kolommen, is er echt geen voordeel ten opzichte van een enkele timestamp kolom. Ik zou me aanpassen:

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;

Als datum en tijd niet de werkelijke date zijn en time datatypes, gebruik to_timestamp() . Gerelateerd:

Zoekopdracht

Dan is de vraag wat eenvoudiger:

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 hier

Genereer een raster van starttijden in de eerste subquery grid , lopend van de eerste tot de laatste kwalificatie rij in het opgegeven tijdsbestek.

Voeg toe aan rijen die in elke partitie vallen met een LATERAL voeg gemiddelden toe en verzamel ze onmiddellijk in de subquery avg . Vanwege de aggregaten is het altijd geeft een rij terug, zelfs als er geen items zijn gevonden. Gemiddelden zijn standaard NULL in dit geval.

Het resultaat omvat alle tijdvakken tussen de eerste en de laatste kwalificatierij in het opgegeven tijdsbestek. Verschillende andere resultaatsamenstellingen zouden ook logisch zijn. Vind ik leuk inclusief alles tijdvakken in het opgegeven tijdsbestek of alleen tijdvakken met werkelijke waarden. Alles mogelijk, ik moest één interpretatie kiezen.

Index

Zorg in ieder geval voor deze index met meerdere kolommen:

CRATE INDEX foo_idx ON tbl (sn, ts);

Of op (sn, ts, vin1, vin2, vin3) om alleen-index scans toe te staan ​​- als aan bepaalde voorwaarden is voldaan en vooral als tabelrijen veel breder zijn dan in de demo.

Nauw verwant:

Gebaseerd op je oorspronkelijke tafel

Zoals gevraagd en verduidelijkt in de opmerking , en later opnieuw bijgewerkt in de vraag om de kolommen mac . op te nemen en loc . Ik neem aan dat je aparte gemiddelden wilt per (mac, loc) .

date en time zijn nog steeds aparte kolommen, vin* kolommen zijn van het type float , en sluit tijdvakken zonder rijen uit:

De bijgewerkte query verplaatst ook de set-retourfunctie generate_series() naar de FROM lijst, die schoner is vóó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;

Maak een expressie-index met meerdere kolommen om dit te ondersteunen:

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

db<>fiddle hier

Maar ik gebruik liever timestamp al die tijd.




  1. Mariadb-verbindingsclient:toegang geweigerd voor gebruiker (met wachtwoord:NEE) op mysql 8.0

  2. Gegevens kopiëren van de ene tabel naar de andere tabel. Databases zijn anders en tabelstructuur is anders

  3. 2 manieren om rijen te retourneren die alleen alfanumerieke tekens bevatten in MariaDB

  4. Verbinding maken met mysql vanuit C# via SSH