Стъпка 1:Освободете ръчните спирачки
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;
Основни точки
-
Заменете запазени думи (в стандартен SQL) във вашите идентификатори.
timestamp
->ts
time
->min_time
-
Тъй като свързването е на идентични имена на колони, можете да използвате по-простия
USING
клаузата в условието за присъединяване:USING (sensor_id, ts)
Въпреки това, след втората таблицаsensor_values_cleaned
е 100% неподходящ за тази заявка, премахнах го изцяло. -
Както @joop вече посъветва, превключете
min()
иto_char()
в първата си колона. По този начин Postgres може да определи минимума от оригиналната стойност на колоната , което обикновено е по-бързо и може да е в състояние да използва индекс. В този конкретен случай, поръчка поdate
също е по-евтино от поръчка чрезtext
, което също ще трябва да вземе предвид правилата за сортиране. -
Подобно съображение се отнася и за вашето
WHERE
условие:WHERE ts::timestamptz>='2013-10-14T00:00:00+00:00'::timestamptzWHERE ts >= '2013-10-14T00:00:00+00:00'::timestamptz::timestamp
Вторият е sargable и може да използва обикновен индекс на
ts
- за голям ефект върху производителността в големи маси! -
Използване на
ts::date
вместоdate_trunc('day', ts)
. По-просто, по-бързо, същият резултат. -
Най-вероятно вашето второ WHERE условие е малко неправилно. По принцип бихте изключили горната граница :
AND ts <= '2013-10-18T00:00:00+00:00' ...
AND ts < '2013-10-18T00:00:00+00:00' ...
-
При смесване на
timestamp
иtimestamptz
човек трябва да е наясно с ефектите. Например вашетоWHERE
условието не прекъсва в 00:00 местно време (освен ако местното време съвпада с UTC). Подробности тук:
Пълно игнориране на часовите зони в Rails и PostgreSQL
Стъпка 2:Вашата заявка
И с това предполагам имате предвид:
...разликата между стойността на най-новите и най-ранните времеви марки ...
В противен случай би било много по-просто.
Използвайте прозоречни функции
за това, по-специално first_value()
и last_value()
. Внимавайте с комбинацията, искате не -стандартна дограма
за last_value() в този случай. Сравнете:
PostgreSQL агрегатна или прозоречна функция за връщане само на последната стойност
Комбинирам това с DISTINCT ON
, което е по-удобно в този случай от GROUP BY
(което ще изисква друго ниво на подзаявка):
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;
Стъпка 3:Обобщена таблица
Въз основа на заявката по-горе използвам crosstab()
от допълнителния модул 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);
Връщания (и много по-бързо от преди):
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