sql >> Database teknologi >  >> RDS >> PostgreSQL

Få forskel på et andet felt mellem første og sidste tidsstempler for gruppering

Trin 1:Slip håndbremserne

SELECT to_char(MIN(ts)::timestamptz, 'YYYY-MM-DD HH24:MI:SS TZ') AS min_time
      ,SUM(CASE WHEN sensor_id = 572 THEN value ELSE 0.0 END) AS nickname1
      ,SUM(CASE WHEN sensor_id = 542 THEN value ELSE 0.0 END) AS nickname2
      ,SUM(CASE WHEN sensor_id = 571 THEN value ELSE 0.0 END) AS nickname3
FROM   sensor_values
-- LEFT JOIN sensor_values_cleaned s2 USING (sensor_id, ts)
WHERE  ts >= '2013-10-14T00:00:00+00:00'::timestamptz::timestamp
AND    ts <  '2013-10-18T00:00:00+00:00'::timestamptz::timestamp
AND    sensor_id IN (572, 542, 571, 540, 541, 573)
GROUP  BY ts::date AS day
ORDER  BY 1;
 

Vigtige punkter

  • Erstat reserverede ord (i standard SQL) i dine identifikatorer.
    timestamp -> ts
    time -> min_time

  • Da joinforbindelsen er på identiske kolonnenavne, kan du bruge den enklere USING klausul i forbindelsestilstanden:USING (sensor_id, ts)
    Men siden den anden tabel sensor_values_cleaned er 100 % irrelevant for denne forespørgsel, fjernede jeg den fuldstændigt.

  • Som @joop allerede har anbefalet, skift min() og to_char() i din første udgangskolonne. På denne måde kan Postgres bestemme minimum fra den oprindelige kolonneværdi , som generelt er hurtigere og muligvis kan bruge et indeks. I dette specifikke tilfælde skal du bestille efter date er også billigere end at bestille med en text , som også skulle tage højde for sammenstillingsregler.

  • En lignende betragtning gælder for din WHERE condition:
    WHERE ts::timestamptz>='2013-10-14T00:00:00+00:00'::timestamptz

    WHERE  ts >= '2013-10-14T00:00:00+00:00'::timestamptz::timestamp
     

    Den anden er sargable og kan bruge et almindeligt indeks på ts - med stor effekt på ydeevnen i store borde!

  • Brug af ts::date i stedet for date_trunc('day', ts) . Enklere, hurtigere, samme resultat.

  • Sandsynligvis er din anden WHERE-tilstand lidt forkert. Generelt ville du udelukke den øvre kant :

    AND    ts <=  '2013-10-18T00:00:00+00:00' ... 

    AND    ts <   '2013-10-18T00:00:00+00:00' ... 
  • Ved blanding af timestamp og timestamptz man skal være opmærksom på virkningerne. For eksempel din WHERE tilstand afbrydes ikke kl. 00:00 lokal tid (undtagen hvis lokal tid falder sammen med UTC). Detaljer her:
    Ignorerer tidszoner helt i Rails og PostgreSQL

Trin 2:Din anmodning

Og med det formoder jeg, at du mener:
...forskellen mellem værdien af de seneste og tidligste tidsstempler ...
Ellers ville det være meget enklere.

Brug vinduefunktioner for det, især first_value() og last_value() . Vær forsigtig med kombinationen, du vil have en ikke -standard vinduesramme for sidste_værdi() i dette tilfælde. Sammenlign:
PostgreSQL-aggregat- eller vinduesfunktion for kun at returnere den sidste værdi

Jeg kombinerer dette med strong>DISTINCT ON , hvilket er mere praktisk i dette tilfælde end GROUP BY (som ville kræve et andet underforespørgselsniveau):

SELECT DISTINCT ON (ts::date, sensor_id)
       ts::date AS day
      ,to_char((min(ts)  OVER (PARTITION BY ts::date))::timestamptz
              ,'YYYY-MM-DD HH24:MI:SS TZ') AS min_time
      ,sensor_id
      ,last_value(value)    OVER (PARTITION BY ts::date, sensor_id ORDER BY ts
                     RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)
       - first_value(value) OVER (PARTITION BY ts::date, sensor_id ORDER BY ts)
                                                                   AS val_range
FROM   sensor_values
WHERE  ts >= '2013-10-14T00:00:00+0'::timestamptz::timestamp
AND    ts <  '2013-10-18T00:00:00+0'::timestamptz::timestamp
AND    sensor_id IN (540, 541, 542, 571, 572, 573)
ORDER  BY ts::date, sensor_id;
 

-> SQLfiddle-demo.

Trin 3:Pivottabel

Med udgangspunkt i forespørgslen ovenfor bruger jeg crosstab() fra det ekstra modul tablefunc :

SELECT * FROM crosstab(
   $$SELECT DISTINCT ON (1,3)
            ts::date AS day
           ,to_char((min(ts) OVER (PARTITION BY ts::date))::timestamptz,'YYYY-MM-DD HH24:MI:SS TZ') AS min_time
           ,sensor_id
           ,last_value(value)    OVER (PARTITION BY ts::date, sensor_id ORDER BY ts RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)
            - first_value(value) OVER (PARTITION BY ts::date, sensor_id ORDER BY ts) AS val_range
     FROM   sensor_values
     WHERE  ts >= '2013-10-14T00:00:00+0'::timestamptz::timestamp
     AND    ts <  '2013-10-18T00:00:00+0'::timestamptz::timestamp
     AND    sensor_id IN (540, 541, 542, 571, 572, 573)
     ORDER  BY 1, 3$$

   ,$$VALUES (540), (541), (542), (571), (572), (573)$$
   )
AS ct (day date, min_time text, s540 numeric, s541 numeric, s542 numeric, s571 numeric, s572 numeric, s573 numeric);
 

Returnerer (og meget). hurtigere end før):

day | min_time | s540 | s541 | s542 | s571 | s572 | s573 ------------+--------------------------+-------+-------+-------+-------+-------+------- 2013-10-14 | 2013-10-14 03:00:00 CEST | 18.82 | 18.98 | 19.97 | 19.47 | 17.56 | 21.27 2013-10-15 | 2013-10-15 00:15:00 CEST | 22.59 | 24.20 | 22.90 | 21.27 | 22.75 | 22.23 2013-10-16 | 2013-10-16 00:16:00 CEST | 23.74 | 22.52 | 22.23 | 23.22 | 23.03 | 22.98 2013-10-17 | 2013-10-17 00:17:00 CEST | 21.68 | 24.54 | 21.15 | 23.58 | 23.04 | 21.94

  1. Hvad skal du gøre, hvis du ikke kan åbne delt objektfil-fejl, når du bruger OCI-versionen af ​​Easysoft Oracle ODBC-driveren?

  2. Beregner du et glidende gennemsnit i MySQL?

  3. Sådan løses FEJL 1060:Dupliker kolonnenavn ved hjælp af Views -> Create View

  4. MySQL SUM-funktion i flere joinforbindelser