From 1d5b97c65ff0b5be1810b6315ee0f23f620e231c Mon Sep 17 00:00:00 2001 From: Dusan Vojacek Date: Thu, 11 Jun 2026 14:30:44 +0200 Subject: [PATCH] =?UTF-8?q?V=C3=BDkon:=20vw=5Flatest=5Finverter=20/=20vw?= =?UTF-8?q?=5Flatest=5Fev=5Fcharger=20p=C5=99epis=20na=20LATERAL?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DISTINCT ON třídilo ~195k/277k řádků hypertable při každém čtení (fn_site_full_status 1.7 s). LATERAL limit 1 per zařízení jde po PK indexu. Ověřeno na živé DB: identické výsledky, inverter 508→56 ms, EV 460→75 ms. EV konektory: discovery za 30 dní (tabulka konektorů neexistuje; mrtvý konektor po 30 dnech z latest pohledu zmizí — vědomá změna sémantiky). Sloupce i pořadí beze změny (create or replace view kompatibilní). Co-Authored-By: Claude Opus 4.8 (1M context) --- db/views/R__058_vw_latest_telemetry.sql | 208 +++++++++++++++--------- 1 file changed, 129 insertions(+), 79 deletions(-) diff --git a/db/views/R__058_vw_latest_telemetry.sql b/db/views/R__058_vw_latest_telemetry.sql index 580aa76..83fe890 100644 --- a/db/views/R__058_vw_latest_telemetry.sql +++ b/db/views/R__058_vw_latest_telemetry.sql @@ -2,90 +2,140 @@ -- R__058_vw_latest_telemetry.sql -- EMS Platform – aktuální stav všech zařízení per lokalita -- Repeatable migration +-- +-- Výkon (audit 2026-06-11): původní DISTINCT ON přes celé hypertable +-- třídilo ~195k (inverter) / ~277k (EV) řádků při každém čtení +-- (fn_site_full_status ~1.7 s). LATERAL limit 1 per zařízení čte jen +-- špičku PK indexu ((inverter_id|charger_id, …, measured_at)). -- ============================================================= -- security_invoker = false: oprávnění na podkladové hypertably nemusí mít ems_anon (PostgREST). -CREATE OR REPLACE VIEW ems.vw_latest_inverter -WITH (security_invoker = false) -AS -SELECT DISTINCT ON (t.inverter_id) - t.site_id, - t.inverter_id, - inv.code AS inverter_code, - t.measured_at, - t.pv_power_w, - t.battery_soc_percent, - t.battery_power_w, - t.grid_power_w, - t.load_power_w, - t.inverter_temp_c, - t.operating_mode, - t.fault_code, - now() - t.measured_at AS data_age, - t.pv1_power_w, - t.pv2_power_w, - t.gen_port_power_w, - t.batt_charge_today_wh, - t.batt_discharge_today_wh, - t.run_state, - t.is_export_limited, - t.pv_derating_flags -FROM ems.telemetry_inverter t -JOIN ems.asset_inverter inv ON inv.id = t.inverter_id -ORDER BY t.inverter_id, t.measured_at DESC; +create or replace view ems.vw_latest_inverter +with (security_invoker = false) +as +select + inv.site_id, + inv.id as inverter_id, + inv.code as inverter_code, + t.measured_at, + t.pv_power_w, + t.battery_soc_percent, + t.battery_power_w, + t.grid_power_w, + t.load_power_w, + t.inverter_temp_c, + t.operating_mode, + t.fault_code, + now() - t.measured_at as data_age, + t.pv1_power_w, + t.pv2_power_w, + t.gen_port_power_w, + t.batt_charge_today_wh, + t.batt_discharge_today_wh, + t.run_state, + t.is_export_limited, + t.pv_derating_flags +from ems.asset_inverter inv +left join lateral ( + select + ti.measured_at, + ti.pv_power_w, + ti.battery_soc_percent, + ti.battery_power_w, + ti.grid_power_w, + ti.load_power_w, + ti.inverter_temp_c, + ti.operating_mode, + ti.fault_code, + ti.pv1_power_w, + ti.pv2_power_w, + ti.gen_port_power_w, + ti.batt_charge_today_wh, + ti.batt_discharge_today_wh, + ti.run_state, + ti.is_export_limited, + ti.pv_derating_flags + from ems.telemetry_inverter ti + where ti.inverter_id = inv.id + order by ti.measured_at desc + limit 1 +) t on true +where t.measured_at is not null; -COMMENT ON VIEW ems.vw_latest_inverter IS -'Nejnovější telemetrická data pro každý střídač. Slouží pro real-time dashboard a health check.'; +comment on view ems.vw_latest_inverter is +'Nejnovější telemetrická data pro každý střídač (LATERAL per-inverter, PK index). Slouží pro real-time dashboard a health check.'; -- ------------------------------------------------------------ -CREATE OR REPLACE VIEW ems.vw_latest_ev_charger -WITH (security_invoker = false) -AS -SELECT DISTINCT ON (t.charger_id, t.connector_id) - t.site_id, - t.charger_id, - ch.code AS charger_code, - t.connector_id, - t.measured_at, - t.status, - t.power_w, - t.energy_kwh, - t.current_a, - t.session_id, - t.error_code, - now() - t.measured_at AS data_age -FROM ems.telemetry_ev_charger t -JOIN ems.asset_ev_charger ch ON ch.id = t.charger_id -ORDER BY t.charger_id, t.connector_id, t.measured_at DESC; +create or replace view ems.vw_latest_ev_charger +with (security_invoker = false) +as +select + ch.site_id, + ch.id as charger_id, + ch.code as charger_code, + conn.connector_id, + t.measured_at, + t.status, + t.power_w, + t.energy_kwh, + t.current_a, + t.session_id, + t.error_code, + now() - t.measured_at as data_age +from ems.asset_ev_charger ch +-- konektory za posledních 30 dní (tabulka konektorů neexistuje; konektor bez +-- telemetrie 30 dní je pro „latest“ dashboard mrtvý) +left join lateral ( + select distinct tc.connector_id + from ems.telemetry_ev_charger tc + where tc.charger_id = ch.id + and tc.measured_at >= now() - interval '30 days' +) conn on true +left join lateral ( + select + te.measured_at, + te.status, + te.power_w, + te.energy_kwh, + te.current_a, + te.session_id, + te.error_code + from ems.telemetry_ev_charger te + where te.charger_id = ch.id + and te.connector_id = conn.connector_id + order by te.measured_at desc + limit 1 +) t on true +where t.measured_at is not null; -COMMENT ON VIEW ems.vw_latest_ev_charger IS -'Nejnovější telemetrická data pro každý konektor EV nabíječky. Slouží pro dashboard a řízení nabíjení.'; +comment on view ems.vw_latest_ev_charger is +'Nejnovější telemetrická data pro každý konektor EV nabíječky (LATERAL per-konektor, PK index). Slouží pro dashboard a řízení nabíjení.'; -- ------------------------------------------------------------ -CREATE OR REPLACE VIEW ems.vw_latest_heat_pump -WITH (security_invoker = false) -AS -SELECT - hp.site_id, - hp.id AS heat_pump_id, - hp.code AS heat_pump_code, - t.measured_at, - t.outdoor_temp_c, - t.tuv_tank_temp_c, - t.water_outlet_temp_c, - t.power_w, - t.operating_mode, - t.cop_actual, - t.defrost_active, - t.alarm_code, - -- Odhadovaný COP pro aktuální venkovní teplotu - ems.fn_cop_estimate(hp.id, t.outdoor_temp_c) AS cop_estimated, - now() - t.measured_at AS data_age -FROM ems.asset_heat_pump hp -LEFT JOIN LATERAL ( - SELECT +create or replace view ems.vw_latest_heat_pump +with (security_invoker = false) +as +select + hp.site_id, + hp.id as heat_pump_id, + hp.code as heat_pump_code, + t.measured_at, + t.outdoor_temp_c, + t.tuv_tank_temp_c, + t.water_outlet_temp_c, + t.power_w, + t.operating_mode, + t.cop_actual, + t.defrost_active, + t.alarm_code, + -- Odhadovaný COP pro aktuální venkovní teplotu + ems.fn_cop_estimate(hp.id, t.outdoor_temp_c) as cop_estimated, + now() - t.measured_at as data_age +from ems.asset_heat_pump hp +left join lateral ( + select thp.measured_at, thp.outdoor_temp_c, thp.tuv_tank_temp_c, @@ -95,12 +145,12 @@ LEFT JOIN LATERAL ( thp.cop_actual, thp.defrost_active, thp.alarm_code - FROM ems.telemetry_heat_pump thp - WHERE thp.heat_pump_id = hp.id - ORDER BY thp.measured_at DESC - LIMIT 1 -) t ON true; + from ems.telemetry_heat_pump thp + where thp.heat_pump_id = hp.id + order by thp.measured_at desc + limit 1 +) t on true; -COMMENT ON VIEW ems.vw_latest_heat_pump IS +comment on view ems.vw_latest_heat_pump is 'Nejnovější telemetrická data pro každé tepelné čerpadlo včetně odhadovaného COP. Slouží pro real-time dashboard a rozhodovací logiku plánování.';