Výkon: vw_latest_inverter / vw_latest_ev_charger přepis na LATERAL

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) <noreply@anthropic.com>
This commit is contained in:
Dusan Vojacek
2026-06-11 14:30:44 +02:00
parent a8b4342099
commit 1d5b97c65f

View File

@@ -2,90 +2,140 @@
-- R__058_vw_latest_telemetry.sql -- R__058_vw_latest_telemetry.sql
-- EMS Platform aktuální stav všech zařízení per lokalita -- EMS Platform aktuální stav všech zařízení per lokalita
-- Repeatable migration -- 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). -- security_invoker = false: oprávnění na podkladové hypertably nemusí mít ems_anon (PostgREST).
CREATE OR REPLACE VIEW ems.vw_latest_inverter create or replace view ems.vw_latest_inverter
WITH (security_invoker = false) with (security_invoker = false)
AS as
SELECT DISTINCT ON (t.inverter_id) select
t.site_id, inv.site_id,
t.inverter_id, inv.id as inverter_id,
inv.code AS inverter_code, inv.code as inverter_code,
t.measured_at, t.measured_at,
t.pv_power_w, t.pv_power_w,
t.battery_soc_percent, t.battery_soc_percent,
t.battery_power_w, t.battery_power_w,
t.grid_power_w, t.grid_power_w,
t.load_power_w, t.load_power_w,
t.inverter_temp_c, t.inverter_temp_c,
t.operating_mode, t.operating_mode,
t.fault_code, t.fault_code,
now() - t.measured_at AS data_age, now() - t.measured_at as data_age,
t.pv1_power_w, t.pv1_power_w,
t.pv2_power_w, t.pv2_power_w,
t.gen_port_power_w, t.gen_port_power_w,
t.batt_charge_today_wh, t.batt_charge_today_wh,
t.batt_discharge_today_wh, t.batt_discharge_today_wh,
t.run_state, t.run_state,
t.is_export_limited, t.is_export_limited,
t.pv_derating_flags t.pv_derating_flags
FROM ems.telemetry_inverter t from ems.asset_inverter inv
JOIN ems.asset_inverter inv ON inv.id = t.inverter_id left join lateral (
ORDER BY t.inverter_id, t.measured_at DESC; 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 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.'; '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 create or replace view ems.vw_latest_ev_charger
WITH (security_invoker = false) with (security_invoker = false)
AS as
SELECT DISTINCT ON (t.charger_id, t.connector_id) select
t.site_id, ch.site_id,
t.charger_id, ch.id as charger_id,
ch.code AS charger_code, ch.code as charger_code,
t.connector_id, conn.connector_id,
t.measured_at, t.measured_at,
t.status, t.status,
t.power_w, t.power_w,
t.energy_kwh, t.energy_kwh,
t.current_a, t.current_a,
t.session_id, t.session_id,
t.error_code, t.error_code,
now() - t.measured_at AS data_age now() - t.measured_at as data_age
FROM ems.telemetry_ev_charger t from ems.asset_ev_charger ch
JOIN ems.asset_ev_charger ch ON ch.id = t.charger_id -- konektory za posledních 30 dní (tabulka konektorů neexistuje; konektor bez
ORDER BY t.charger_id, t.connector_id, t.measured_at DESC; -- 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 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í.'; '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 create or replace view ems.vw_latest_heat_pump
WITH (security_invoker = false) with (security_invoker = false)
AS as
SELECT select
hp.site_id, hp.site_id,
hp.id AS heat_pump_id, hp.id as heat_pump_id,
hp.code AS heat_pump_code, hp.code as heat_pump_code,
t.measured_at, t.measured_at,
t.outdoor_temp_c, t.outdoor_temp_c,
t.tuv_tank_temp_c, t.tuv_tank_temp_c,
t.water_outlet_temp_c, t.water_outlet_temp_c,
t.power_w, t.power_w,
t.operating_mode, t.operating_mode,
t.cop_actual, t.cop_actual,
t.defrost_active, t.defrost_active,
t.alarm_code, t.alarm_code,
-- Odhadovaný COP pro aktuální venkovní teplotu -- Odhadovaný COP pro aktuální venkovní teplotu
ems.fn_cop_estimate(hp.id, t.outdoor_temp_c) AS cop_estimated, ems.fn_cop_estimate(hp.id, t.outdoor_temp_c) as cop_estimated,
now() - t.measured_at AS data_age now() - t.measured_at as data_age
FROM ems.asset_heat_pump hp from ems.asset_heat_pump hp
LEFT JOIN LATERAL ( left join lateral (
SELECT select
thp.measured_at, thp.measured_at,
thp.outdoor_temp_c, thp.outdoor_temp_c,
thp.tuv_tank_temp_c, thp.tuv_tank_temp_c,
@@ -95,12 +145,12 @@ LEFT JOIN LATERAL (
thp.cop_actual, thp.cop_actual,
thp.defrost_active, thp.defrost_active,
thp.alarm_code thp.alarm_code
FROM ems.telemetry_heat_pump thp from ems.telemetry_heat_pump thp
WHERE thp.heat_pump_id = hp.id where thp.heat_pump_id = hp.id
ORDER BY thp.measured_at DESC order by thp.measured_at desc
LIMIT 1 limit 1
) t ON true; ) 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. '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í.'; Slouží pro real-time dashboard a rozhodovací logiku plánování.';