É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->tstime->min_time -
Puisque la jointure est sur des noms de colonnes identiques, vous pouvez utiliser le plus simple
USINGclause dans la condition de jointure :USING (sensor_id, ts)
Cependant, depuis la deuxième tablesensor_values_cleanedest 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 pardateest é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
WHEREcondition :WHERE ts::timestamptz>='2013-10-14T00:00:00+00:00'::timestamptzWHERE ts >= '2013-10-14T00:00:00+00:00'::timestamptz::timestampLe second est sargable et peut utiliser un index simple sur
ts- au grand effet sur les performances dans les grandes tables ! -
Utilisation de
ts::dateau 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
timestampettimestamptzil faut être conscient des effets. Par exemple, votreWHEREcondition 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