FEAT : conf objets timescale OK

This commit is contained in:
Raymond Bourges 2026-03-13 16:58:15 +01:00
parent 6f5293d325
commit aaa63a4a8d
5 changed files with 216 additions and 76 deletions

141
notes.md
View File

@ -14,53 +14,9 @@ exit
cd /tmp cd /tmp
scp grab-4:/tmp/backup_postgres.dump . scp grab-4:/tmp/backup_postgres.dump .
pg_restore -c -x -t mesure -d postgres -h localhost -U postgres -W backup_postgres.dump pg_restore -c -x -t mesure -d postgres -h localhost -U postgres -W backup_postgres.dump
pg_restore -c -x -I mesure_idx1 -d postgres -h localhost -U postgres -W backup_postgres.dump # pg_restore -c -x -I mesure_idx1 -d postgres -h localhost -U postgres -W backup_postgres.dump
pg_restore -c -x -I mesure_idx2 -d postgres -h localhost -U postgres -W backup_postgres.dump # pg_restore -c -x -I mesure_idx2 -d postgres -h localhost -U postgres -W backup_postgres.dump
``` pg_restore -c -x -t captation -d postgres -h localhost -U postgres -W backup_postgres.dump
### Création d'une hypertable
```sql
CREATE TABLE mesure_ng (
"date" TIMESTAMPTZ,
id_captation INT NOT NULL,
valeur float4 NOT NULL
) WITH (
tsdb.hypertable,
timescaledb.segmentby = 'id_captation',
timescaledb.orderby='date DESC'
)
INSERT INTO mesure_ng ("date", id_captation, valeur)
SELECT "date", id_captation, valeur
FROM mesure
```
### Compression des données (stockage en colonnes)
`SELECT add_columnstore_policy('mesure_ng', INTERVAL '15 days')`
### Ex. de requête
```sql
SELECT
time_bucket('1 hour', "date") AS heure,
round(
average(
time_weight('linear', "date", valeur)
)
) AS moyenne_conso,
COUNT(*) AS nombre_de_mesures
FROM
mesure_ng
WHERE
"date" BETWEEN '2026-03-01 00:00:00' AND '2026-03-31 23:59:59'
and id_captation = 59 -- puissance PAC
GROUP BY
heure
ORDER BY
heure;
``` ```
### Exploit ### Exploit
@ -73,40 +29,73 @@ WHERE hypertable_name = 'mesure_ng'
`SELECT * FROM timescaledb_information.jobs` `SELECT * FROM timescaledb_information.jobs`
### Continuous aggregates (WIP) ### WIP
<https://www.tigerdata.com/docs/use-timescale/latest/continuous-aggregates/create-a-continuous-aggregate> #### Grafana
* CREATE MATERIALIZED VIEW mixe de
* CALL add_continuous_aggregate_policy
* supprimer les "vieilles" données de mesure_ng (add_retention_policy ?)
#### Exemples sur le range :
##### Gauges ```sql
SELECT *
CREATE MATERIALIZED VIEW sensor_hourly FROM (
WITH (timescaledb.continuous)
AS
SELECT
time_bucket('1 hour', time) AS bucket,
sensor_id,
average(
time_weight('linear', "date", valeur)
) AS moyenne_temporelle,
percentile_agg(value) AS pct
FROM sensor_data
GROUP BY bucket, sensor_id;
Puis
SELECT SELECT
bucket, "date" AS time,
sensor_id, valeur
approx_percentile(0.95, pct) AS p95, FROM mesure_gauge
approx_percentile(0.99, pct) AS p99 WHERE $__timeFilter("date")
FROM sensor_hourly; AND $__range_s < 86400
UNION ALL
SELECT
"date" AS time,
average(valeur_temporelle)
FROM mesure_gauge_heure
WHERE $__timeFilter("date")
AND $__range_s BETWEEN 86400 AND 2592000
UNION ALL
SELECT
"date" AS time,
average(valeur_temporelle)
FROM mesure_gauge_jour
WHERE $__timeFilter("date")
AND $__range_s > 2592000
) t
ORDER BY time
```
et où est dans le passé
```sql
SELECT *
FROM (
-- données récentes → brut
SELECT
mg."date" AS time,
mg.valeur
FROM mesure_gauge mg
WHERE $__timeFilter(mg."date")
AND $__to > now() - interval '7 days'
UNION ALL
-- données anciennes → agrégées
SELECT
mgh."date" AS time,
average(mgh.valeur_temporelle) AS valeur
FROM mesure_gauge_heure mgh
WHERE $__timeFilter(mgh."date")
AND $__to <= now() - interval '7 days'
) t
ORDER BY time;
```
##### Index
delta(counter_agg(time, value))

3
sql/1_type_captation.sql Normal file
View File

@ -0,0 +1,3 @@
-- pour différencier les capteurs (Gauges) des index (Compteurs)
ALTER TABLE public.captation ADD "type" varchar DEFAULT 'G' NOT NULL;
COMMENT ON COLUMN public.captation."type" IS 'G pour Gauge, C pour Compteur (index)';

71
sql/2_gauge.sql Normal file
View File

@ -0,0 +1,71 @@
CREATE TABLE mesure_gauge ( -- TODO ; revoir précudes d'injection de données pour faire la différence entre gauge et compteur
"date" TIMESTAMPTZ,
id_captation INT NOT NULL,
valeur float4 NOT NULL
) WITH (
tsdb.hypertable,
timescaledb.segmentby = 'id_captation',
timescaledb.orderby='date DESC'
);
INSERT INTO mesure_gauge ("date", id_captation, valeur)
SELECT m."date", m.id_captation, m.valeur
FROM mesure m
WHERE m.id_captation IN (
SELECT c.id_captation
FROM captation c
WHERE c."type" = 'G' -- TODO : prévoir cette info dans captation
);
SELECT add_retention_policy('mesure_gauge', INTERVAL '6 months'); -- on ne garde les données brutes que 6 mois
CREATE MATERIALIZED VIEW mesure_gauge_heure -- valeurs par heure
WITH (timescaledb.continuous) AS
SELECT
id_captation,
time_bucket(INTERVAL '1 HOUR', mg."date") AS "date",
time_weight('linear', mg."date", mg.valeur) AS valeur_temporelle,
MAX(mg.valeur),
MIN(mg.valeur)
FROM mesure_gauge mg
GROUP BY id_captation, time_bucket(INTERVAL '1 hour', mg."date");
SELECT add_continuous_aggregate_policy( -- On met à jour les données tt les h en relisant les données brutes entre 1 mois et 1 jour
'mesure_gauge_heure',
start_offset => INTERVAL '1 month',
end_offset => INTERVAL '1 day',
schedule_interval => INTERVAL '1 hour'
);
SELECT add_retention_policy('mesure_gauge_heure', INTERVAL '5 years'); -- on ne garde les données par h que 5 ans
CREATE MATERIALIZED VIEW mesure_gauge_jour -- valeurs par jour
WITH (timescaledb.continuous) AS
SELECT
id_captation,
time_bucket(INTERVAL '1 DAY', mg."date") AS "date",
rollup(valeur_temporelle) AS valeur_temporelle,
MAX(mg.max),
MIN(mg.min)
FROM mesure_gauge_heure mg
GROUP BY id_captation, time_bucket(INTERVAL '1 DAY', mg."date");
SELECT add_continuous_aggregate_policy( -- On met à jour les données tt les h en relisant les données brutes entre 1 mois et 1 jour
'mesure_gauge_jour',
start_offset => INTERVAL '1 month',
end_offset => INTERVAL '1 day',
schedule_interval => INTERVAL '1 hour'
);
SELECT add_retention_policy('mesure_gauge_jour', INTERVAL '20 years'); -- on ne garde les données par h que 20 ans
-- ex. select
select
mgh.id_captation,
time_bucket(INTERVAL '1 DAY', mgh."date") AS "date", -- 1 jour et pas seulement une heure
average(rollup(valeur_temporelle)) AS moyenne -- rollup pour avour le concaténation des valeur_temporelle
from mesure_gauge_heure mgh
where mgh.id_captation = 59 -- puissance PAC
group by mgh.id_captation, time_bucket(INTERVAL '1 DAY', mgh."date")
order by "date" desc
limit 1000;

70
sql/3_compteur.sql Normal file
View File

@ -0,0 +1,70 @@
CREATE TABLE mesure_compteur ( -- TODO ; revoir précudes d'injection de données pour faire la différence entre gauge et compteur
"date" TIMESTAMPTZ,
id_captation INT NOT NULL,
valeur float4 NOT NULL
) WITH (
tsdb.hypertable,
timescaledb.segmentby = 'id_captation',
timescaledb.orderby='date DESC'
);
INSERT INTO mesure_compteur ("date", id_captation, valeur)
SELECT m."date", m.id_captation, m.valeur
FROM mesure m
WHERE m.id_captation IN (
SELECT c.id_captation
FROM captation c
WHERE c."type" = 'C' -- TODO : prévoir cette info dans captation
);
SELECT add_retention_policy('mesure_compteur', INTERVAL '6 months'); -- on ne garde les données brutes que 6 mois
CREATE MATERIALIZED VIEW mesure_compteur_heure -- valeurs par heure
WITH (timescaledb.continuous) AS
SELECT
id_captation,
time_bucket(INTERVAL '1 hour', mc."date") AS "date",
counter_agg(mc."date", mc.valeur) AS valeur_temporelle
FROM mesure_compteur mc
GROUP BY
id_captation,
time_bucket(INTERVAL '1 hour', mc."date");
SELECT add_continuous_aggregate_policy('mesure_compteur_heure', -- On met à jour les données tt les h en relisant les données brutes entre 1 mois et 1 jour
start_offset => INTERVAL '1 month',
end_offset => INTERVAL '1 day',
schedule_interval => INTERVAL '1 hour');
SELECT add_retention_policy('mesure_compteur_heure', INTERVAL '5 years'); -- on ne garde les données par h que 5 ans
CREATE MATERIALIZED VIEW mesure_compteur_jour -- valeurs par jour
WITH (timescaledb.continuous) AS
SELECT
id_captation,
time_bucket(INTERVAL '1 DAY', mg."date") AS "date",
rollup(valeur_temporelle) AS valeur_temporelle
FROM mesure_compteur_heure mg
GROUP BY id_captation, time_bucket(INTERVAL '1 DAY', mg."date");
SELECT add_continuous_aggregate_policy( -- On met à jour les données tt les h en relisant les données brutes entre 1 mois et 1 jour
'mesure_compteur_jour',
start_offset => INTERVAL '1 month',
end_offset => INTERVAL '1 day',
schedule_interval => INTERVAL '1 hour'
);
SELECT add_retention_policy('mesure_compteur_jour', INTERVAL '20 years'); -- on ne garde les données par h que 20 ans
-- ex. select
SELECT
mch.id_captation,
time_bucket(INTERVAL '1 DAY', mch."date") AS "date", -- 1 jour et pas seulement une heure
delta(rollup(mch.valeur_temporelle)) AS consommation,
rate(rollup(mch.valeur_temporelle)) * 3600 AS debit_moyen,
irate_left(rollup(mch.valeur_temporelle)) * 3600 AS debit_instantane,
first_val(rollup(mch.valeur_temporelle)) AS index_debut,
last_val(rollup(mch.valeur_temporelle)) AS index_fin
FROM mesure_compteur_heure mch
where mch.id_captation = 89 -- HP
group by mch.id_captation, time_bucket(INTERVAL '1 DAY', mch."date")
order by "date" desc

7
sql/4_drop.sql Normal file
View File

@ -0,0 +1,7 @@
DROP MATERIALIZED VIEW IF EXISTS mesure_gauge_jour;
DROP MATERIALIZED VIEW IF EXISTS mesure_gauge_heure;
DROP TABLE IF EXISTS mesure_gauge;
DROP MATERIALIZED VIEW IF EXISTS mesure_compteur_jour;
DROP MATERIALIZED VIEW IF EXISTS mesure_compteur_jour;
DROP TABLE IF EXISTS mesure_compteur;