Stap 1:Zet de handremmen los
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;
Belangrijkste punten
-
Vervang gereserveerde woorden (in standaard SQL) in uw identifiers.
timestamp->tstime->min_time -
Aangezien de join op identieke kolomnamen staat, kunt u de eenvoudigere
USINGclausule in de join-voorwaarde:USING (sensor_id, ts)
Echter, sinds de tweede tabelsensor_values_cleanedis 100% niet relevant voor deze zoekopdracht, ik heb hem volledig verwijderd. -
Zoals @joop al adviseerde, schakel
min()ento_char()in uw eerste geplaatste kolom. Op deze manier kan Postgres het minimum bepalen uit de oorspronkelijke kolomwaarde , die over het algemeen sneller is en mogelijk een index kan gebruiken. In dit specifieke geval, bestellen voordateis ook goedkoper dan bestellen met eentext, die ook rekening zou moeten houden met sorteerregels. -
Een soortgelijke overweging geldt voor uw
WHEREvoorwaarde:WAAR ts::timestamptz>='2013-10-14T00:00:00+00:00'::timestamptzWHERE ts >= '2013-10-14T00:00:00+00:00'::timestamptz::timestampDe tweede is sargable en kan een gewone index gebruiken op
ts- met een groot effect op de prestaties aan grote tafels! -
ts::dategebruiken in plaats vandate_trunc('day', ts). Eenvoudiger, sneller, hetzelfde resultaat. -
Hoogstwaarschijnlijk is uw tweede WHERE-voorwaarde enigszins onjuist. Over het algemeen zou u de bovenrand uitsluiten :
AND ts <= '2013-10-18T00:00:00+00:00' ...AND ts < '2013-10-18T00:00:00+00:00' ... -
Bij het mixen van
timestampentimestamptzmen moet zich bewust zijn van de effecten. Bijvoorbeeld uwWHEREconditie stopt niet om 00:00 lokale tijd (behalve als lokale tijd samenvalt met UTC). Details hier:
Tijdzones volledig negeren in Rails en PostgreSQL
Stap 2:Uw verzoek
En daarmee bedoel ik denk ik:
...het verschil tussen de waarde van de laatste en vroegste tijdstempels ...
Anders zou het veel eenvoudiger zijn.
Gebruik vensterfuncties
daarvoor in het bijzonder first_value() en last_value() . Wees voorzichtig met de combinatie, u wilt een niet -standaard raamkozijn
voor last_value() in dit geval. Vergelijk:
PostgreSQL-aggregatie- of vensterfunctie om alleen de laatste waarde te retourneren
Ik combineer dit met DISTINCT ON
, wat in dit geval handiger is dan GROUP BY (waarvoor een ander subqueryniveau nodig zou zijn):
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;
Stap 3:Draaitabel
Voortbouwend op de bovenstaande query gebruik ik crosstab()
uit de aanvullende module 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);
Retourneert (en veel) sneller dan voorheen):
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