second version
This commit is contained in:
75
db/routines/R__fn_fill_forecast_accuracy.sql
Normal file
75
db/routines/R__fn_fill_forecast_accuracy.sql
Normal file
@@ -0,0 +1,75 @@
|
||||
CREATE OR REPLACE FUNCTION ems.fn_fill_forecast_accuracy(
|
||||
p_site_id INT,
|
||||
p_lookback_hours INT DEFAULT 48
|
||||
)
|
||||
RETURNS INT
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
DECLARE
|
||||
v_count INT := 0;
|
||||
BEGIN
|
||||
INSERT INTO ems.forecast_accuracy (
|
||||
site_id, pv_array_id, interval_start, run_id,
|
||||
forecast_power_w, forecast_created_at, lead_time_hours,
|
||||
actual_power_w, actual_filled_at,
|
||||
error_w, error_pct
|
||||
)
|
||||
SELECT
|
||||
fpr.site_id,
|
||||
fpr.pv_array_id,
|
||||
fpi.interval_start,
|
||||
fpi.run_id,
|
||||
fpi.power_w AS forecast_power_w,
|
||||
fpr.created_at AS forecast_created_at,
|
||||
ROUND(
|
||||
EXTRACT(EPOCH FROM (fpi.interval_start - fpr.created_at))
|
||||
/ 3600.0, 2
|
||||
) AS lead_time_hours,
|
||||
slot.avg_actual_w::INT AS actual_power_w,
|
||||
now() AS actual_filled_at,
|
||||
fpi.power_w - COALESCE(slot.avg_actual_w::INT, 0) AS error_w,
|
||||
CASE
|
||||
WHEN slot.avg_actual_w IS NOT NULL
|
||||
AND slot.avg_actual_w > 0
|
||||
THEN ROUND(
|
||||
(fpi.power_w::NUMERIC - slot.avg_actual_w::NUMERIC)
|
||||
/ slot.avg_actual_w::NUMERIC * 100,
|
||||
4
|
||||
)
|
||||
ELSE NULL
|
||||
END AS error_pct
|
||||
FROM ems.forecast_pv_interval fpi
|
||||
JOIN ems.forecast_pv_run fpr ON fpr.id = fpi.run_id
|
||||
JOIN ems.asset_pv_array pa ON pa.id = fpr.pv_array_id
|
||||
LEFT JOIN LATERAL (
|
||||
SELECT AVG(
|
||||
CASE
|
||||
WHEN pa.controllable = false THEN ti.gen_port_power_w::NUMERIC
|
||||
ELSE (COALESCE(ti.pv1_power_w, 0) + COALESCE(ti.pv2_power_w, 0))::NUMERIC
|
||||
END
|
||||
) AS avg_actual_w
|
||||
FROM ems.telemetry_inverter ti
|
||||
WHERE ti.site_id = fpr.site_id
|
||||
AND ti.measured_at >= fpi.interval_start
|
||||
AND ti.measured_at < fpi.interval_start + INTERVAL '15 minutes'
|
||||
) slot ON true
|
||||
WHERE fpr.site_id = p_site_id
|
||||
AND fpr.status = 'ok'
|
||||
AND fpi.interval_start < now() - INTERVAL '15 minutes'
|
||||
AND fpi.interval_start >= now() - make_interval(hours => p_lookback_hours)
|
||||
ON CONFLICT (run_id, interval_start) DO UPDATE SET
|
||||
actual_power_w = EXCLUDED.actual_power_w,
|
||||
actual_filled_at = EXCLUDED.actual_filled_at,
|
||||
error_w = EXCLUDED.error_w,
|
||||
error_pct = EXCLUDED.error_pct;
|
||||
|
||||
GET DIAGNOSTICS v_count = ROW_COUNT;
|
||||
RETURN v_count;
|
||||
END;
|
||||
$$;
|
||||
|
||||
COMMENT ON FUNCTION ems.fn_fill_forecast_accuracy(INT, INT) IS
|
||||
'Doplní skutečné hodnoty výroby do forecast_accuracy z telemetrie.
|
||||
Volat každých 15 minut (spolu s audit_filler) pro inkrementální plnění.
|
||||
p_lookback_hours: kolik hodin zpět zpracovat (default 48h pro catch-up).
|
||||
Pro první backfill: SELECT ems.fn_fill_forecast_accuracy(2, 8760) -- 1 rok';
|
||||
Reference in New Issue
Block a user