Changes between Version 10 and Version 11 of QueryOptimization


Ignore:
Timestamp:
06/14/26 18:10:05 (5 days ago)
Author:
231025
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • QueryOptimization

    v10 v11  
    11= Фаза 3: Индекси и оптимизација на прашалници =
    2 
    3 
    4 
    52
    63[[BR]]
     
    118=== Поглед 1: v_site_zapisnici ===
    129'''Брз преглед на сите записници по лице, без join-ови.'''[[BR]]
     10Овој поглед овозможува брзо пронаоѓање на основните информации за издадените записници со цел оптимизација на почетните пребарувања. Наменет е за приказ на листи со записи во корисничкиот интерфејс каде што корисникот филтрира по прекршител или полицаец. Притоа, погледот извлекува податоци директно само од табелата `Zapisnik`, избегнувајќи сложени спојувања.[[BR]]
    1311Се пребарува по: `EMBG_Prekrsuvach` (или `EMBG_Policaec`).
    1412{{{#!sql
    1513CREATE OR REPLACE VIEW v_site_zapisnici AS
    1614SELECT id_na_zapisnik, datum, vreme, lokacija, Potpis,
    17        id_slucaj, EMBG_Prekrsuvach, Vozilo_Broj_Sasija, EMBG_Policaec
     15id_slucaj, EMBG_Prekrsuvach, Vozilo_Broj_Sasija, EMBG_Policaec
    1816FROM Zapisnik;
    1917}}}
    2018
    2119=== Поглед 2: v_zapisnici_detalno ===
    22 '''Целосни детали за конкретен записник: прекршител, возило, полицаец, прекршок, износ казна и статус уплата.'''[[BR]]
     20'''Целосни детали за конкретен записник: прекршител, возило, полицаец, прекршок, износ на казната и статус на уплатата.'''[[BR]]
     21Погледот овозможува детален и сеопфатен приказ на сите информации поврзани со еден конкретен сообраќаен налог. Се користи во апликацијата кога операторот или граѓанинот сака да ги отвори сите детали за поединечен записник. За да се постигне ова, во view се спојува главната табела `Zapisnik` со табелите `Gragjanin` (за прекршителот и полицаецот), `Vozilo`, `Policaec`, `Stavka_Zapisnik`, `Prekrsok`, `Kazna` и `Uplata`.[[BR]]
    2322Се пребарува по: `id_na_zapisnik` / `EMBG_Prekrsuvach`.
    2423{{{#!sql
    2524CREATE OR REPLACE VIEW v_zapisnici_detalno AS
    2625SELECT z.id_na_zapisnik, z.datum, z.vreme, z.lokacija, z.id_slucaj,
    27        g.EMBG AS embg_prekrsitel, g.ime AS prekrsitel_ime, g.prezime AS prekrsitel_prezime,
    28        v.broj_na_sasija, v.model AS vozilo_model,
    29        pol.EMBG_P AS embg_policaec, g_p.ime AS policaec_ime, g_p.prezime AS policaec_prezime,
    30        p.ime AS prekrsok, k.iznos_kazna, u.iznos AS iznos_uplata, u.status AS status_uplata
     26g.EMBG AS embg_prekrsitel, g.ime AS prekrsitel_ime, g.prezime AS prekrsitel_prezime,
     27v.broj_na_sasija, v.model AS vozilo_model,
     28pol.EMBG_P AS embg_policaec, g_p.ime AS policaec_ime, g_p.prezime AS policaec_prezime,
     29p.ime AS prekrsok, k.iznos_kazna, u.iznos AS iznos_uplata, u.status AS status_uplata
    3130FROM Zapisnik z
    3231LEFT JOIN Gragjanin g        ON z.EMBG_Prekrsuvach = g.EMBG
     
    4241=== Поглед 3: v_neplateni_kazni ===
    4342'''Сите неплатени казни на даден граѓанин, со износ за наплата и колку денови се веќе неплатени.'''[[BR]]
     43Овој поглед служи за брза финансиска проверка на прекршочните долгови кои сè уште не се подмирени од страна на граѓаните. Се користи при генерирање на налог за судска постапка, односно за неплатени обврски со пресметан рок на доцнење. Погледот се генерира преку филтрирано спојување на табелите `Uplata`, `Zapisnik`, `Gragjanin`, `Stavka_Zapisnik`, `Prekrsok` и `Kazna`.[[BR]]
    4444Се пребарува по: `EMBG_prekrsitel`.
    4545{{{#!sql
    4646CREATE OR REPLACE VIEW v_neplateni_kazni AS
    4747SELECT z.id_na_zapisnik, z.datum AS datum_prekrsok, z.lokacija,
    48        g.EMBG AS embg_prekrsitel, g.ime AS ime_prekrsitel, g.prezime AS prezime_prekrsitel,
    49        p.ime AS prekrsok, k.iznos_kazna, u.iznos AS iznos_za_naplata,
    50        (CURRENT_DATE - z.datum) AS denovi_neplateno
     48g.EMBG AS embg_prekrsitel, g.ime AS ime_prekrsitel, g.prezime AS prezime_prekrsitel,
     49p.ime AS prekrsok, k.iznos_kazna, u.iznos AS iznos_za_naplata,
     50(CURRENT_DATE - z.datum) AS denovi_neplateno
    5151FROM Uplata u
    5252JOIN Zapisnik z         ON u.id_zapisnik = z.id_na_zapisnik
     
    5959
    6060=== Поглед 4: v_povtoreni_prekrsoci_mv ===
    61 '''Граѓани што повторуваат ист тип прекршок (`COUNT > 1`) — колку пати, прв и последен пат, вкупен износ казни.'''[[BR]]
     61'''Граѓани што повторуваат ист тип прекршок (COUNT > 1)-колку пати, прв и последен пат, вкупен износ казни.'''[[BR]]
     62Ова е аналитички поглед наменет за следење и идентификување на прекршители, односно возачи кои постојано го повторуваат истиот прекршок. Се користи за статистички извештаи на МВР со цел таргетирање на критичните прекршители на патиштата. Погледот прави напредна агрегација и групирање со спојување на табелите `Zapisnik`, `Stavka_Zapisnik`, `Prekrsok`, `Kazna` и на крајот го врзува резултатот со табелата `Gragjanin`.[[BR]]
    6263Се пребарува по: `EMBG`.
    6364{{{#!sql
    6465CREATE MATERIALIZED VIEW v_povtoreni_prekrsoci_mv AS
    6566WITH Prekrsoci_Stats AS (
    66     SELECT z.EMBG_Prekrsuvach, sz.id_na_prekrsok,
    67            COUNT(*) AS broj_povtori, MIN(z.datum) AS prv_pat, MAX(z.datum) AS posleden_pat,
    68            SUM(k.iznos_kazna) AS vkupen_iznos_kazni
    69     FROM Zapisnik z
    70     JOIN Stavka_Zapisnik sz ON z.id_na_zapisnik = sz.id_na_zapisnik
    71     JOIN Prekrsok p         ON sz.id_na_prekrsok = p.id_prekrsok
    72     JOIN Kazna k            ON p.id_kazna = k.id_kazna
    73     GROUP BY z.EMBG_Prekrsuvach, sz.id_na_prekrsok
    74     HAVING COUNT(*) > 1
     67SELECT z.EMBG_Prekrsuvach, sz.id_na_prekrsok,
     68COUNT() AS broj_povtori, MIN(z.datum) AS prv_pat, MAX(z.datum) AS posleden_pat,
     69SUM(k.iznos_kazna) AS vkupen_iznos_kazni
     70FROM Zapisnik z
     71JOIN Stavka_Zapisnik sz ON z.id_na_zapisnik = sz.id_na_zapisnik
     72JOIN Prekrsok p         ON sz.id_na_prekrsok = p.id_prekrsok
     73JOIN Kazna k            ON p.id_kazna = k.id_kazna
     74GROUP BY z.EMBG_Prekrsuvach, sz.id_na_prekrsok
     75HAVING COUNT() > 1
    7576)
    7677SELECT g.EMBG, g.ime, g.prezime, p.id_prekrsok, p.ime AS prekrsok,
    77        ps.broj_povtori, ps.prv_pat, ps.posleden_pat, ps.vkupen_iznos_kazni
     78ps.broj_povtori, ps.prv_pat, ps.posleden_pat, ps.vkupen_iznos_kazni
    7879FROM Prekrsoci_Stats ps
    7980JOIN Gragjanin g ON ps.EMBG_Prekrsuvach = g.EMBG
     
    8384=== Поглед 5: v_policaici_prosek_zapisnici_mv ===
    8485'''Статистики по полицаец: вкупно записници, прв/последен, активни месеци и просечен број записници по месец.'''[[BR]]
    85 Се пребарува по: `EMBG_P` (дополнително по име на станица).
     86Погледот овозможува евалуација на ефикасноста и активноста на полициските службеници на терен низ подолг временски период. Наменет е за внатрешна контрола и менаџмент на полициските станици за следење на просечната продуктивност по месец. Се потпира на статистичка агрегација над табелата `Zapisnik`, која потоа се спојува со табелите `Policaec` и `Gragjanin` за комплетирање на профилот.[[BR]]
     87Се пребарува по: EMBG_P.
    8688{{{#!sql
    8789CREATE MATERIALIZED VIEW v_policaici_prosek_zapisnici_mv AS
    8890WITH Policaec_Stats AS (
    89     SELECT EMBG_Policaec, COUNT(id_na_zapisnik) AS vkupno_zapisnici,
    90            MIN(datum) AS prv_zapisnik, MAX(datum) AS posleden_zapisnik
    91     FROM Zapisnik
    92     GROUP BY EMBG_Policaec
     91SELECT EMBG_Policaec, COUNT(id_na_zapisnik) AS vkupno_zapisnici,
     92MIN(datum) AS prv_zapisnik, MAX(datum) AS posleden_zapisnik
     93FROM Zapisnik
     94GROUP BY EMBG_Policaec
    9395)
    9496SELECT pol.EMBG_P, pol.broj_na_znacka, g.ime AS policaec_ime, g.prezime AS policaec_prezime,
    95        ps.vkupno_zapisnici, ps.prv_zapisnik, ps.posleden_zapisnik,
    96        GREATEST(DATE_PART('year', AGE(ps.posleden_zapisnik, ps.prv_zapisnik)) * 12 +
    97                 DATE_PART('month', AGE(ps.posleden_zapisnik, ps.prv_zapisnik)), 1) AS aktivni_meseci,
    98        ROUND(ps.vkupno_zapisnici::numeric /
    99              GREATEST(DATE_PART('year', AGE(ps.posleden_zapisnik, ps.prv_zapisnik)) * 12 +
    100                       DATE_PART('month', AGE(ps.posleden_zapisnik, ps.prv_zapisnik)), 1)::numeric, 2) AS prosek_zapisnici_po_mesec
     97ps.vkupno_zapisnici, ps.prv_zapisnik, ps.posleden_zapisnik,
     98GREATEST(DATE_PART('year', AGE(ps.posleden_zapisnik, ps.prv_zapisnik)) * 12 +
     99DATE_PART('month', AGE(ps.posleden_zapisnik, ps.prv_zapisnik)), 1) AS aktivni_meseci,
     100ROUND(ps.vkupno_zapisnici::numeric /
     101GREATEST(DATE_PART('year', AGE(ps.posleden_zapisnik, ps.prv_zapisnik)) * 12 +
     102DATE_PART('month', AGE(ps.posleden_zapisnik, ps.prv_zapisnik)), 1)::numeric, 2) AS prosek_zapisnici_po_mesec
    101103FROM Policaec_Stats ps
    102104JOIN Policaec pol ON ps.EMBG_Policaec = pol.EMBG_P
     
    106108=== Поглед 6: v_istorija_gragjanin_mv ===
    107109'''Целосен профил на граѓанин: вкупно прекршоци, платено, долг, прв/последен прекршок и број различни типови.'''[[BR]]
     110Овој поглед нуди целосно прекршочно и финансиско досие за секој граѓанин во системот на едно место. Се користи при пребарување на профил на граѓанин во апликацијата за веднаш да се прикаже неговата историја, вкупниот долг и направените уплати. Погледот комбинира и спојува две посебни агрегации од табелите `Zapisnik`, `Stavka_Zapisnik`, `Prekrsok` и `Kazna` од една страна, и табелата `Uplata` од друга страна, поврзувајќи ги на крај со табелата `Gragjanin`.[[BR]]
    108111Се пребарува по: `EMBG`.
    109112{{{#!sql
    110113CREATE MATERIALIZED VIEW v_istorija_gragjanin_mv AS
    111114WITH Zapisnik_Stats AS (
    112     SELECT EMBG_Prekrsuvach,
    113            COUNT(DISTINCT z.id_na_zapisnik) AS vkupno_zapisnici,
    114            MIN(z.datum) AS prv_prekrsok, MAX(z.datum) AS posleden_prekrsok,
    115            COUNT(sz.id_stavka) AS vkupno_prekrshoci,
    116            COUNT(DISTINCT sz.id_na_prekrsok) AS razlichni_tipovi,
    117            COALESCE(SUM(k.iznos_kazna), 0) AS vkupen_iznos_kazni
    118     FROM Zapisnik z
    119     LEFT JOIN Stavka_Zapisnik sz ON z.id_na_zapisnik = sz.id_na_zapisnik
    120     LEFT JOIN Prekrsok p         ON sz.id_na_prekrsok = p.id_prekrsok
    121     LEFT JOIN Kazna k            ON p.id_kazna = k.id_kazna
    122     GROUP BY EMBG_Prekrsuvach
     115SELECT EMBG_Prekrsuvach,
     116COUNT(DISTINCT z.id_na_zapisnik) AS vkupno_zapisnici,
     117MIN(z.datum) AS prv_prekrsok, MAX(z.datum) AS posleden_prekrsok,
     118COUNT(sz.id_stavka) AS vkupno_prekrshoci,
     119COUNT(DISTINCT sz.id_na_prekrsok) AS razlichni_tipovi,
     120COALESCE(SUM(k.iznos_kazna), 0) AS vkupen_iznos_kazni
     121FROM Zapisnik z
     122LEFT JOIN Stavka_Zapisnik sz ON z.id_na_zapisnik = sz.id_na_zapisnik
     123LEFT JOIN Prekrsok p         ON sz.id_na_prekrsok = p.id_prekrsok
     124LEFT JOIN Kazna k            ON p.id_kazna = k.id_kazna
     125GROUP BY EMBG_Prekrsuvach
    123126),
    124127Uplata_Stats AS (
    125     SELECT z.EMBG_Prekrsuvach,
    126            COALESCE(SUM(CASE WHEN u.status = 'Plateno'  THEN u.iznos END), 0) AS vkupno_plateno,
    127            COALESCE(SUM(CASE WHEN u.status = 'Neplateno' THEN u.iznos END), 0) AS vkupen_dolg,
    128            COUNT(CASE WHEN u.status = 'Neplateno' THEN u.id_uplata END) AS broj_neplateni
    129     FROM Uplata u
    130     JOIN Zapisnik z ON u.id_zapisnik = z.id_na_zapisnik
    131     GROUP BY z.EMBG_Prekrsuvach
     128SELECT z.EMBG_Prekrsuvach,
     129COALESCE(SUM(CASE WHEN u.status = 'Plateno'  THEN u.iznos END), 0) AS vkupno_plateno,
     130COALESCE(SUM(CASE WHEN u.status = 'Neplateno' THEN u.iznos END), 0) AS vkupen_dolg,
     131COUNT(CASE WHEN u.status = 'Neplateno' THEN u.id_uplata END) AS broj_neplateni
     132FROM Uplata u
     133JOIN Zapisnik z ON u.id_zapisnik = z.id_na_zapisnik
     134GROUP BY z.EMBG_Prekrsuvach
    132135)
    133136SELECT g.EMBG, g.ime, g.prezime, g.datum_ragjanje,
    134        zs.vkupno_zapisnici, zs.vkupno_prekrshoci, zs.razlichni_tipovi, zs.vkupen_iznos_kazni,
    135        COALESCE(us.vkupno_plateno, 0) AS vkupno_plateno,
    136        COALESCE(us.vkupen_dolg, 0)    AS vkupen_dolg,
    137        COALESCE(us.broj_neplateni, 0) AS broj_neplateni,
    138        zs.prv_prekrsok, zs.posleden_prekrsok
     137zs.vkupno_zapisnici, zs.vkupno_prekrshoci, zs.razlichni_tipovi, zs.vkupen_iznos_kazni,
     138COALESCE(us.vkupno_plateno, 0) AS vkupno_plateno,
     139COALESCE(us.vkupen_dolg, 0)    AS vkupen_dolg,
     140COALESCE(us.broj_neplateni, 0) AS broj_neplateni,
     141zs.prv_prekrsok, zs.posleden_prekrsok
    139142FROM Gragjanin g
    140143JOIN Zapisnik_Stats zs ON g.EMBG = zs.EMBG_Prekrsuvach
     
    142145}}}
    143146
    144 
    145147[[BR]]
    146148== 2. Оптимизација ==
    147 За секој поглед го измеривме почетното време преку `EXPLAIN ANALYZE`, ја идентификувавме најбавната операција и применивме оптимизација. Кај пребарувањата по клуч тоа е индекс, а кај аналитичките погледи — материјализација.
    148 
    149 === Поглед 1 — v_site_zapisnici ===
    150 Најбавна операција: `Seq Scan on zapisnik` (се чита целата табела). Решение — индекс на колоната по која се филтрира:
     149За секој поглед го измеривме почетното време, ја идентификувавме најбавната операција и применивме оптимизација. Кај пребарувањата по клуч тоа е индекс, а кај аналитичките погледи-материјализација.
     150
     151=== Поглед 1: v_site_zapisnici ===
     152Најбавна операција: `Seq Scan` on `Zapisnik` (се чита целата табела). Решение: индекс на колоната по која се филтрира:
    151153{{{#!sql
    152154CREATE INDEX idx_zapisnik_prekrsuvach ON Zapisnik(EMBG_Prekrsuvach);
     
    167169[[Image(vew1-index.png, 700px)]]
    168170
    169 === Поглед 2 — v_zapisnici_detalno ===
    170 Најбавна операција: `Parallel Seq Scan on uplata` (~2.000.000 редови). Решение — индекси на join-колоните:
     171=== Поглед 2: v_zapisnici_detalno ===
     172Најбавна операција: Parallel Seq Scan on uplata (~2.000.000 редови). [[BR]] [[BR]]Решение: индекси на join-колоните:
    171173{{{#!sql
    172174CREATE INDEX idx_stavka_zapisnik_id_zapisnik ON Stavka_Zapisnik(id_na_zapisnik);
     
    188190[[Image(view2-index.png, 700px)]]
    189191
    190 === Поглед 3 — v_neplateni_kazni ===
    191 Најбавна операција: `Seq Scan on uplata`. Join-условот (`u.id_zapisnik = z.id_na_zapisnik`) е ист како кај Поглед 2, затоа не е потребен нов индекс — се реискористува постоечкиот `idx_uplata_id_zapisnik`.
     192=== Поглед 3: v_neplateni_kazni ===
     193Најбавна операција: Seq Scan on uplata. Join-условот (u.id_zapisnik = z.id_na_zapisnik) е ист како кај Поглед 2, затоа не е потребен нов индекс, се реискористува постоечкиот idx_uplata_id_zapisnik.
    192194
    193195'''Прашалник кој го тестираме:'''
     
    205207[[Image(view3-index.png, 700px)]]
    206208
    207 === Поглед 4 — v_povtoreni_prekrsoci_mv ===
    208 Аналитички поглед — `GROUP BY` / `HashAggregate` над цела `Zapisnik`, па индекс не помага. Решение: материјализација (дефиницијата е во делот Погледи).
     209=== Поглед 4: v_povtoreni_prekrsoci_mv ===
     210Аналитички поглед-GROUP BY / HashAggregate над цела Zapisnik, па индекс не помага. Решение: материјализација.
    209211
    210212'''Прашалник кој го тестираме:'''
     
    219221
    220222'''Потоа''' (материјализиран поглед)[[BR]]
    221 Со материјализиран поглед добиваме 388ms време на читање.
     223време на извршување: 388ms
    222224[[Image(view4-materialized.png, 700px)]] [[BR]]
    223 Освежувањето (`REFRESH`) трае 1m27s.
     225Освежувањето (REFRESH) трае 1m27s.
    224226{{{#!sql
    225227REFRESH MATERIALIZED VIEW v_povtoreni_prekrsoci_mv;
    226228}}}
    227229
    228 === Поглед 5 — v_policaici_prosek_zapisnici_mv ===
    229 Аналитички поглед — агрегација (`COUNT`, `MIN`, `MAX`) групирана по полицаец над цела `Zapisnik`, индекс не носи добивка. Решение: материјализација (дефиницијата е во делот Погледи).
     230=== Поглед 5: v_policaici_prosek_zapisnici_mv ===
     231Аналитички поглед-агрегација (COUNT, MIN, MAX) групирана по полицаец над цела Zapisnik, индекс не носи добивка. Решение: материјализација.
    230232
    231233'''Прашалник кој го тестираме:'''
     
    240242
    241243'''Потоа''' (материјализиран поглед)[[BR]]
    242 Со материјализиран поглед добиваме <1s време на читање.
     244време на извршување: <1s
    243245[[Image(view5-materialized.png, 700px)]] [[BR]]
    244 Освежувањето (`REFRESH`) трае <2s.
     246Освежувањето (REFRESH) трае <2s.
    245247{{{#!sql
    246248REFRESH MATERIALIZED VIEW v_policaici_prosek_zapisnici_mv;
    247249}}}
    248250
    249 === Поглед 6 — v_istorija_gragjanin_mv ===
    250 Аналитички поглед — две одделни агрегации (над `Zapisnik`/`Stavka_Zapisnik` и над `Uplata`) па join, двете страни се скенираат во целост. Решение: материјализација (дефиницијата е во делот Погледи).
     251=== Поглед 6: v_istorija_gragjanin_mv ===
     252Аналитички поглед-две одделни агрегации (над Zapisnik/Stavka_Zapisnik и над Uplata) па join, двете страни се скенираат во целост. Решение: материјализација.
    251253
    252254'''Прашалник кој го тестираме:'''
     
    261263
    262264'''Потоа''' (материјализиран поглед)[[BR]]
    263 Со материјализиран поглед добиваме <1s време на читање.
     265време на извршување: <1s
    264266[[Image(view6-materialized.png, 700px)]] [[BR]]
    265 Освежувањето (`REFRESH`) трае 2m17s.
     267Освежувањето (REFRESH) трае 2m17s.
    266268{{{#!sql
    267269REFRESH MATERIALIZED VIEW v_istorija_gragjanin_mv;
    268270}}}
    269 
    270271
    271272[[BR]]