Etapa 1:solte os freios de mão
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;
Pontos principais
-
Substitua palavras reservadas (no SQL padrão) em seus identificadores.timestamp
->ts
time
->min_time
-
Como a junção está em nomes de colunas idênticos, você pode usar oUSING
cláusula na condição de junção:USING (sensor_id, ts)
No entanto, como a segunda tabelasensor_values_cleaned
é 100% irrelevante para esta consulta, eu a removi completamente.
-
Como @joop já aconselhou, mudemin()
eto_char()
em sua primeira coluna de saída. Dessa forma, o Postgres pode determinar o mínimo do valor da coluna original , que geralmente é mais rápido e pode utilizar um índice. Neste caso específico, pedir pordate
também é mais barato do que pedir por umtext
, que também teria que considerar as regras de agrupamento.
-
Uma consideração semelhante se aplica ao seuWHERE
condição:WHERE ts::timestamptz>='2013-10-14T00:00:00+00:00'::timestamptz
WHERE ts >= '2013-10-14T00:00:00+00:00'::timestamptz::timestamp
O segundo é sargable e pode utilizar um índice simples emts
- com grande efeito no desempenho em grandes mesas!
-
Usandots::date
em vez dedate_trunc('day', ts)
. Mais simples, mais rápido, mesmo resultado.
-
Muito provavelmente sua segunda condição WHERE está um pouco incorreta. Geralmente, você excluiria a borda superior :AND ts <= '2013-10-18T00:00:00+00:00' ...
AND ts < '2013-10-18T00:00:00+00:00' ...
-
Ao misturartimestamp
etimestamptz
é preciso estar ciente dos efeitos. Por exemplo, seuWHERE
condição não é cortada às 00:00 hora local (exceto se a hora local coincidir com UTC). Detalhes aqui:
Ignorando completamente os fusos horários no Rails e PostgreSQL
Etapa 2:sua solicitação
E com isso suponho que você queira dizer:
...a diferença entre o valor de os timestamps mais recentes e mais antigos ...
Caso contrário, seria muito mais simples.
Use funções de janela para isso, em particular
first_value()
e last_value()
. Cuidado com a combinação, você quer um não -moldura de janela padrão
para last_value() neste caso. Compare:Agregado PostgreSQL ou função de janela para retornar apenas o último valor
Eu combino isso com
DISTINCT ON
, que é mais conveniente neste caso do que GROUP BY
(o que precisaria de outro nível de subconsulta):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;
-> Demonstração do SQLfiddle.
Etapa 3:tabela dinâmica
Com base na consulta acima, uso
crosstab()
do módulo adicional 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);
Devoluções (e muito mais rápido do que antes):
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