Étape 1 :desserrez les freins à main
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;
Points majeurs
-
Remplacer mots réservés (en SQL standard) dans vos identifiants.
timestamp
->ts
time
->min_time
-
Puisque la jointure est sur des noms de colonnes identiques, vous pouvez utiliser le plus simple
USING
clause dans la condition de jointure :USING (sensor_id, ts)
Cependant, depuis la deuxième tablesensor_values_cleaned
est 100 % sans rapport avec cette requête, je l'ai entièrement supprimée. -
Comme @joop l'a déjà conseillé, changez
min()
etto_char()
dans votre première colonne de sortie. De cette façon, Postgres peut déterminer le minimum à partir de la valeur de la colonne d'origine , qui est généralement plus rapide et peut utiliser un index. Dans ce cas précis, commander pardate
est également moins cher que de commander par untext
, qui devrait également tenir compte des règles de classement. -
Une considération similaire s'applique à votre
WHERE
condition :WHERE ts::timestamptz>='2013-10-14T00:00:00+00:00'::timestamptzWHERE ts >= '2013-10-14T00:00:00+00:00'::timestamptz::timestamp
Le second est sargable et peut utiliser un index simple sur
ts
- au grand effet sur les performances dans les grandes tables ! -
Utilisation de
ts::date
au lieu dedate_trunc('day', ts)
. Plus simple, plus rapide, même résultat. -
Très probablement, votre deuxième condition WHERE est légèrement incorrecte. Généralement, vous devez exclure la bordure supérieure :
AND ts <= '2013-10-18T00:00:00+00:00' ...
AND ts < '2013-10-18T00:00:00+00:00' ...
-
Lors du mélange de
timestamp
ettimestamptz
il faut être conscient des effets. Par exemple, votreWHERE
condition ne coupe pas à 00:00 heure locale (sauf si l'heure locale coïncide avec UTC). Détails ici :
Ignorer complètement les fuseaux horaires dans Rails et PostgreSQL
Étape 2 :Votre demande
Et par là, je suppose que vous voulez dire :
...la différence entre la valeur de les horodatages les plus récents et les plus anciens...
Sinon ce serait beaucoup plus simple.
Utilisez fonctions de fenêtre
pour cela, en particulier first_value()
et last_value()
. Attention à la combinaison, vous voulez un non -cadre de fenêtre standard
pour last_value() dans ce cas. Comparez :
Fonction d'agrégat ou de fenêtre PostgreSQL pour renvoyer uniquement la dernière valeur
Je combine cela avec DISTINCT ON
, ce qui est plus pratique dans ce cas que GROUP BY
(ce qui nécessiterait un autre niveau de sous-requête) :
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;
Étape 3 :tableau croisé dynamique
En m'appuyant sur la requête ci-dessus, j'utilise crosstab()
depuis le module complémentaire 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);
Renvoie (et beaucoup plus vite qu'avant):
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