| 320 | === p_event_risk_overview === |
| 321 | {{{ |
| 322 | CREATE OR REPLACE PROCEDURE p_event_risk_overview( |
| 323 | IN p_event_id int, |
| 324 | IN p_actor_id int, |
| 325 | IN p_user_id int, |
| 326 | IN p_from_date date DEFAULT NULL, |
| 327 | IN p_to_date date DEFAULT NULL, |
| 328 | INOUT p_cur refcursor DEFAULT NULL |
| 329 | ) |
| 330 | LANGUAGE plpgsql |
| 331 | AS $$ |
| 332 | BEGIN |
| 333 | IF p_cur IS NULL THEN p_cur := 'cur_event_risk_overview'; END IF; |
| 334 | |
| 335 | OPEN p_cur FOR |
| 336 | WITH |
| 337 | -- Кој актер и која локација се поврзани со настанот |
| 338 | event_context AS ( |
| 339 | SELECT ed.global_event_id, ed.actor_id, ed.location_id |
| 340 | FROM event_details ed |
| 341 | WHERE ed.global_event_id = p_event_id |
| 342 | OR ed.actor_id = p_actor_id |
| 343 | ), |
| 344 | -- Нотификации поврзани со корисник или настан |
| 345 | notif AS ( |
| 346 | SELECT n.notification_id, n.user_id, n.notification_date, n.event_id |
| 347 | FROM notifications n |
| 348 | WHERE (n.user_id = p_user_id AND n.notification_date BETWEEN COALESCE(p_from_date,'1900-01-01') AND COALESCE(p_to_date, now()::date)) |
| 349 | OR (n.event_id = p_event_id) |
| 350 | ), |
| 351 | -- Прогнози поврзани со настанот/актерот |
| 352 | preds AS ( |
| 353 | SELECT pr.prediction_id, pr.event_id, pr.actor_id, pr.predicted_date, pr.confidence |
| 354 | FROM predictions pr |
| 355 | WHERE pr.event_id = p_event_id |
| 356 | OR pr.actor_id = p_actor_id |
| 357 | ), |
| 358 | -- Аналитика за настан + актер по датуми |
| 359 | analytics AS ( |
| 360 | SELECT ea.event_id, ea.actor_id, ea.date, ea.metric_value |
| 361 | FROM event_analytics ea |
| 362 | WHERE ea.event_id = p_event_id |
| 363 | AND ea.actor_id = p_actor_id |
| 364 | AND ea.date BETWEEN COALESCE(p_from_date,'1900-01-01') AND COALESCE(p_to_date, now()::date) |
| 365 | ), |
| 366 | -- Ризици помеѓу парови актери |
| 367 | risks AS ( |
| 368 | SELECT cr.actor1_id, cr.actor2_id, cr.predicted_date, cr.risk_score |
| 369 | FROM conflict_risk cr |
| 370 | WHERE (cr.actor1_id = p_actor_id OR cr.actor2_id = p_actor_id) |
| 371 | AND cr.predicted_date BETWEEN COALESCE(p_from_date,'1900-01-01') AND COALESCE(p_to_date, now()::date) |
| 372 | ) |
| 373 | SELECT jsonb_build_object( |
| 374 | 'event_id', p_event_id, |
| 375 | 'actor_id', p_actor_id, |
| 376 | 'notifications', COALESCE(jsonb_agg(DISTINCT notif), '[]'::jsonb), |
| 377 | 'predictions', COALESCE(jsonb_agg(DISTINCT preds), '[]'::jsonb), |
| 378 | 'analytics', COALESCE(jsonb_agg(DISTINCT analytics), '[]'::jsonb), |
| 379 | 'risks', COALESCE(jsonb_agg(DISTINCT risks), '[]'::jsonb) |
| 380 | ) AS j |
| 381 | FROM notif, preds, analytics, risks; |
| 382 | END; |
| 383 | $$; |
| 384 | }}} |
| 385 | |
| 386 | == Објаснување на Execution Plan == |
| 387 | |
| 388 | При повик на `p_event_risk_overview` со `EXPLAIN (ANALYZE, BUFFERS)`, PostgreSQL користи индекс скенирања за оптимален пристап: |
| 389 | |
| 390 | * **event_details** → `Index Scan` на `ix_event_details_event` или `ix_event_details_actor` за брзо наоѓање актери/локации поврзани со настанот. |
| 391 | * **notifications** → `Index Scan` на `ix_notifications_user` (композитен индекс по user/date) или `ix_notifications_event` за пребарување на кориснички нотификации. |
| 392 | * **predictions** → `Index Scan` на `ix_predictions_event` или `ix_predictions_actor` за селекција на предвидувања. |
| 393 | * **event_analytics** → `Index Scan` на `ix_analytics_event_actor` со дополнително филтрирање по `date`. |
| 394 | * **conflict_risk** → `Index Scan` на `ix_conflict_pair_date` за проверка на ризици меѓу актери во даден временски интервал. |
| 395 | |
| 396 | Ова покажува дека секоја табела се пристапува преку соодветен индекс наместо со целосно скенирање, |
| 397 | што значително ја зголемува брзината и ја намалува потрошувачката на ресурси при сложени аналитички пребарувања. |
| 398 | |