diff --git a/notes.md b/notes.md index 80bd0f6..02f2d9f 100644 --- a/notes.md +++ b/notes.md @@ -14,53 +14,9 @@ exit cd /tmp 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 -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 -``` - -### 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; +# 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 -t captation -d postgres -h localhost -U postgres -W backup_postgres.dump ``` ### Exploit @@ -73,40 +29,73 @@ WHERE hypertable_name = 'mesure_ng' `SELECT * FROM timescaledb_information.jobs` -### Continuous aggregates (WIP) +### WIP - +#### Grafana -* CREATE MATERIALIZED VIEW -* CALL add_continuous_aggregate_policy -* supprimer les "vieilles" données de mesure_ng (add_retention_policy ?) +mixe de -#### Exemples +sur le range : -##### Gauges - -CREATE MATERIALIZED VIEW sensor_hourly -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 +```sql +SELECT * +FROM ( SELECT - bucket, - sensor_id, - approx_percentile(0.95, pct) AS p95, - approx_percentile(0.99, pct) AS p99 -FROM sensor_hourly; + "date" AS time, + valeur +FROM mesure_gauge +WHERE $__timeFilter("date") +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)) \ No newline at end of file diff --git a/sql/1_type_captation.sql b/sql/1_type_captation.sql new file mode 100644 index 0000000..22720b8 --- /dev/null +++ b/sql/1_type_captation.sql @@ -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)'; \ No newline at end of file diff --git a/sql/2_gauge.sql b/sql/2_gauge.sql new file mode 100644 index 0000000..5b82735 --- /dev/null +++ b/sql/2_gauge.sql @@ -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; \ No newline at end of file diff --git a/sql/3_compteur.sql b/sql/3_compteur.sql new file mode 100644 index 0000000..5b1c7f2 --- /dev/null +++ b/sql/3_compteur.sql @@ -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 \ No newline at end of file diff --git a/sql/4_drop.sql b/sql/4_drop.sql new file mode 100644 index 0000000..1e36f9c --- /dev/null +++ b/sql/4_drop.sql @@ -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;