Changes between Initial Version and Version 1 of DatabaseProgramming


Ignore:
Timestamp:
06/12/26 20:27:50 (8 days ago)
Author:
231025
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • DatabaseProgramming

    v1 v1  
     1= Фаза 4: Функции, процедури и тригери =
     2
     3Во оваа фаза се надоградуваме со функции и процедури што ќе помогнат дел од честите операции во базата — отворање записник, додавање прекршок, наплата на казна, додавање категорија на возачка — да се извршуваат преку еден повик, со сите потребни проверки веќе вградени во нив. Исто така искористивме и тригери за работите што треба да се случуваат автоматски, како пресметката на вкупниот износ во уплатата, без рачно ажурирање.
     4
     5
     6[[BR]]
     7== 1. Тригери ==
     8
     9=== Тригер: Автоматска пресметка на износот во Uplata ===
     10
     11Секој пат кога ќе се додаде, измени или избрише ставка во `Stavka_Zapisnik`, тригерот ја пресметува вкупната сума на сите казни за тој записник и ја запишува во соодветната уплата. Така износот во `Uplata` секогаш е точен и не мора рачно да го ажурираме.
     12
     13{{{#!sql
     14CREATE OR REPLACE FUNCTION azuriraj_iznos_uplata()
     15    RETURNS TRIGGER
     16    LANGUAGE plpgsql
     17AS $$
     18DECLARE
     19    v_id_zapisnik int;
     20    v_nov_iznos   numeric(10,2);
     21BEGIN
     22    IF TG_OP = 'DELETE' THEN
     23        v_id_zapisnik := OLD.id_na_zapisnik;
     24    ELSE
     25        v_id_zapisnik := NEW.id_na_zapisnik;
     26    END IF;
     27
     28    SELECT COALESCE(SUM(k.iznos_kazna), 0)
     29    INTO v_nov_iznos
     30    FROM Stavka_Zapisnik sz
     31    JOIN Prekrsok p ON sz.id_na_prekrsok = p.id_prekrsok
     32    JOIN Kazna    k ON p.id_kazna        = k.id_kazna
     33    WHERE sz.id_na_zapisnik = v_id_zapisnik;
     34
     35    UPDATE Uplata
     36    SET iznos = v_nov_iznos
     37    WHERE id_zapisnik = v_id_zapisnik
     38      AND status = 'Neplateno';
     39
     40    IF TG_OP = 'DELETE' THEN
     41        RETURN OLD;
     42    ELSE
     43        RETURN NEW;
     44    END IF;
     45END;
     46$$;
     47
     48CREATE OR REPLACE TRIGGER trigger_azuriraj_iznos_uplata
     49    AFTER INSERT OR UPDATE OR DELETE ON Stavka_Zapisnik
     50    FOR EACH ROW
     51    EXECUTE FUNCTION azuriraj_iznos_uplata();
     52}}}
     53
     54
     55[[BR]]
     56== 2. Процедури ==
     57
     58=== Процедура 1: kreiraj_zapisnik_so_prekrsok ===
     59Отвора нов записник со прв прекршок и веднаш креира празна уплата со статус `Neplateno`. Износот го пополнува тригерот штом ќе се вметне првата ставка. Прво проверува дали постојат граѓанинот, полицаецот, возилото, прекршокот и случајот.
     60
     61{{{#!sql
     62CREATE OR REPLACE PROCEDURE kreiraj_zapisnik_so_prekrsok(
     63    p_embg_prekrsuvac char(13),
     64    p_broj_sasija     varchar(17),
     65    p_embg_policaec   char(13),
     66    p_lokacija        varchar(100),
     67    p_id_slucaj       int,
     68    p_id_prekrsok     int,
     69    p_vreme           time    DEFAULT CURRENT_TIME,
     70    p_datum           date    DEFAULT CURRENT_DATE,
     71    p_potpis          boolean DEFAULT false
     72)
     73    LANGUAGE plpgsql
     74AS $$
     75DECLARE
     76    v_id_zapisnik int;
     77BEGIN
     78    IF NOT EXISTS (SELECT 1 FROM Gragjanin WHERE EMBG = p_embg_prekrsuvac) THEN
     79        RAISE EXCEPTION 'Gragjaninot so EMBG % ne postoi!', p_embg_prekrsuvac;
     80    END IF;
     81    IF NOT EXISTS (SELECT 1 FROM Policaec WHERE EMBG_P = p_embg_policaec) THEN
     82        RAISE EXCEPTION 'Policaecot so EMBG % ne postoi!', p_embg_policaec;
     83    END IF;
     84    IF NOT EXISTS (SELECT 1 FROM Vozilo WHERE broj_na_sasija = p_broj_sasija) THEN
     85        RAISE EXCEPTION 'Voziloto so broj na sasija % ne postoi!', p_broj_sasija;
     86    END IF;
     87    IF NOT EXISTS (SELECT 1 FROM Prekrsok WHERE id_prekrsok = p_id_prekrsok) THEN
     88        RAISE EXCEPTION 'Prekrsokot so id % ne postoi!', p_id_prekrsok;
     89    END IF;
     90    IF NOT EXISTS (SELECT 1 FROM Slucaj WHERE id_slucaj = p_id_slucaj) THEN
     91        RAISE EXCEPTION 'Slucajot so id % ne postoi!', p_id_slucaj;
     92    END IF;
     93
     94    INSERT INTO Zapisnik (vreme, datum, lokacija, Potpis,
     95        id_slucaj, EMBG_Prekrsuvach, Vozilo_Broj_Sasija, EMBG_Policaec)
     96    VALUES (p_vreme, p_datum, p_lokacija, p_potpis,
     97        p_id_slucaj, p_embg_prekrsuvac, p_broj_sasija, p_embg_policaec)
     98    RETURNING id_na_zapisnik INTO v_id_zapisnik;
     99
     100    INSERT INTO Uplata (iznos, status, Uplatil_Gragjanin, id_zapisnik)
     101    VALUES (0, 'Neplateno', p_embg_prekrsuvac, v_id_zapisnik);
     102
     103    INSERT INTO Stavka_Zapisnik (reden_broj, id_na_zapisnik, id_na_prekrsok)
     104    VALUES (1, v_id_zapisnik, p_id_prekrsok);
     105
     106    RAISE NOTICE 'Kreiran zapisnik so id % so prv prekrsok %.', v_id_zapisnik, p_id_prekrsok;
     107END;
     108$$;
     109}}}
     110
     111Повик:
     112{{{#!sql
     113CALL kreiraj_zapisnik_so_prekrsok(
     114    '1508004480145', 'CE8C5F1D5AA4C0871', '1609966470237',
     115    'Bulevar Partizanski Odredi', 1, 1
     116);
     117}}}
     118
     119
     120=== Процедура 2: dodadi_stavka_zapisnik ===
     121Додава нов прекршок (ставка) на веќе постоечки записник и сам го одредува следниот реден број. Не дозволува додавање ако записникот е веќе платен.
     122
     123{{{#!sql
     124CREATE OR REPLACE PROCEDURE dodadi_stavka_zapisnik(
     125    p_id_zapisnik int,
     126    p_id_prekrsok int
     127)
     128    LANGUAGE plpgsql
     129AS $$
     130DECLARE
     131    v_reden_broj     int;
     132    v_status_uplata  varchar(30);
     133BEGIN
     134    IF NOT EXISTS (SELECT 1 FROM Zapisnik WHERE id_na_zapisnik = p_id_zapisnik) THEN
     135        RAISE EXCEPTION 'Zapisnikot so id % ne postoi!', p_id_zapisnik;
     136    END IF;
     137    IF NOT EXISTS (SELECT 1 FROM Prekrsok WHERE id_prekrsok = p_id_prekrsok) THEN
     138        RAISE EXCEPTION 'Prekrsokot so id % ne postoi!', p_id_prekrsok;
     139    END IF;
     140
     141    SELECT status INTO v_status_uplata
     142    FROM Uplata WHERE id_zapisnik = p_id_zapisnik;
     143
     144    IF v_status_uplata = 'Plateno' THEN
     145        RAISE EXCEPTION 'Zapisnik % e vekje platen, ne moze da se dodavaat novi stavki!', p_id_zapisnik;
     146    END IF;
     147
     148    SELECT COALESCE(MAX(reden_broj), 0) + 1 INTO v_reden_broj
     149    FROM Stavka_Zapisnik WHERE id_na_zapisnik = p_id_zapisnik;
     150
     151    INSERT INTO Stavka_Zapisnik (reden_broj, id_na_zapisnik, id_na_prekrsok)
     152    VALUES (v_reden_broj, p_id_zapisnik, p_id_prekrsok);
     153
     154    RAISE NOTICE 'Dodadena stavka so reden_broj % vo zapisnik %.', v_reden_broj, p_id_zapisnik;
     155END;
     156$$;
     157}}}
     158
     159Повик:
     160{{{#!sql
     161CALL dodadi_stavka_zapisnik(30000005, 3);
     162}}}
     163
     164
     165=== Процедура 3: plati_kazna ===
     166Ја плаќа казната за даден записник — го менува статусот во `Plateno`, го запишува датумот и начинот на плаќање. Проверува дали уплатата постои, дали веќе е платена, дали има износ за наплата и дали начинот на плаќање е валиден.
     167
     168{{{#!sql
     169CREATE OR REPLACE PROCEDURE plati_kazna(
     170    p_id_zapisnik    int,
     171    p_nacin_plakanje varchar(30)
     172)
     173    LANGUAGE plpgsql
     174AS $$
     175DECLARE
     176    v_status varchar(30);
     177    v_iznos  numeric(10,2);
     178BEGIN
     179    SELECT status, iznos INTO v_status, v_iznos
     180    FROM Uplata WHERE id_zapisnik = p_id_zapisnik;
     181
     182    IF v_status IS NULL THEN
     183        RAISE EXCEPTION 'Ne postoi uplata za zapisnik %!', p_id_zapisnik;
     184    END IF;
     185    IF v_status = 'Plateno' THEN
     186        RAISE EXCEPTION 'Kaznata za zapisnik % e vekje platena!', p_id_zapisnik;
     187    END IF;
     188    IF v_iznos <= 0 THEN
     189        RAISE EXCEPTION 'Zapisnikot % nema stavki/kazni za naplata!', p_id_zapisnik;
     190    END IF;
     191    IF p_nacin_plakanje NOT IN ('E-bankarstvo', 'Platezna karticka', 'Gotovo/Uplatnica') THEN
     192        RAISE EXCEPTION 'Nepoznat nacin na plakanje: %.', p_nacin_plakanje;
     193    END IF;
     194
     195    UPDATE Uplata
     196    SET status         = 'Plateno',
     197        datum_uplata   = CURRENT_DATE,
     198        nacin_plakanje = p_nacin_plakanje
     199    WHERE id_zapisnik = p_id_zapisnik;
     200
     201    RAISE NOTICE 'Uspesno naplatena kazna od % za zapisnik % (%).', v_iznos, p_id_zapisnik, p_nacin_plakanje;
     202END;
     203$$;
     204}}}
     205
     206Повик:
     207{{{#!sql
     208CALL plati_kazna(30000005, 'E-bankarstvo');
     209}}}
     210
     211
     212=== Процедура 4: dodadi_kategorija_na_vozacka ===
     213Кога возач полага дополнителна категорија (на пр. има B, полага C), ја додава на возачката. Проверува дали дозволата постои и не е истечена, дали категоријата постои и не е веќе додадена, и дали датумот на полагање е валиден.
     214
     215{{{#!sql
     216CREATE OR REPLACE PROCEDURE dodadi_kategorija_na_vozacka(
     217    p_broj_dozvola    varchar(30),
     218    p_id_kategorija   int,
     219    p_datum_polaganje date DEFAULT CURRENT_DATE
     220)
     221    LANGUAGE plpgsql
     222AS $$
     223DECLARE
     224    v_datum_vaznost_do date;
     225    v_datum_izdavanje  date;
     226    v_kod_kategorija   varchar(3);
     227BEGIN
     228    SELECT datum_vaznost_do, datum_izdavanje
     229    INTO v_datum_vaznost_do, v_datum_izdavanje
     230    FROM Vozacka_dozvola WHERE broj_dozvola = p_broj_dozvola;
     231
     232    IF v_datum_vaznost_do IS NULL THEN
     233        RAISE EXCEPTION 'Vozackata dozvola so broj % ne postoi!', p_broj_dozvola;
     234    END IF;
     235    IF v_datum_vaznost_do < CURRENT_DATE THEN
     236        RAISE EXCEPTION 'Vozackata dozvola % e istechena (vaznost do %)!', p_broj_dozvola, v_datum_vaznost_do;
     237    END IF;
     238
     239    SELECT kod INTO v_kod_kategorija
     240    FROM Kategorija WHERE id_kategorija = p_id_kategorija;
     241
     242    IF v_kod_kategorija IS NULL THEN
     243        RAISE EXCEPTION 'Kategorijata so id % ne postoi!', p_id_kategorija;
     244    END IF;
     245
     246    IF EXISTS (
     247        SELECT 1 FROM Kategorija_Vozacka_dozvola
     248        WHERE broj_dozvola = p_broj_dozvola AND id_kategorija = p_id_kategorija
     249    ) THEN
     250        RAISE EXCEPTION 'Kategorijata % vekje e dodadena na vozackata %!', v_kod_kategorija, p_broj_dozvola;
     251    END IF;
     252
     253    IF p_datum_polaganje > CURRENT_DATE THEN
     254        RAISE EXCEPTION 'Datumot na polaganje % ne moze da bide vo idnina!', p_datum_polaganje;
     255    END IF;
     256    IF p_datum_polaganje < v_datum_izdavanje THEN
     257        RAISE EXCEPTION 'Datumot na polaganje % ne moze da bide pred izdavanjeto na vozackata (%).',
     258            p_datum_polaganje, v_datum_izdavanje;
     259    END IF;
     260
     261    INSERT INTO Kategorija_Vozacka_dozvola (id_kategorija, broj_dozvola, datum_polaganje)
     262    VALUES (p_id_kategorija, p_broj_dozvola, p_datum_polaganje);
     263
     264    RAISE NOTICE 'Uspesno dodadena kategorija % na vozackata %.', v_kod_kategorija, p_broj_dozvola;
     265END;
     266$$;
     267}}}
     268
     269Повик:
     270{{{#!sql
     271CALL dodadi_kategorija_na_vozacka('L8207294', 7);
     272CALL dodadi_kategorija_na_vozacka('L8207294', 9, '2025-11-15');
     273}}}
     274
     275
     276[[BR]]
     277== 3. Дополнителна функција: azuriraj_kazna_plakanja ==
     278Функција што ги обработува задоцнетите уплати наеднаш: на неплатените постари од 2 недели им го зголемува износот за 50%, а тие постари од 2 месеци ги префрла во статус `Sudska_postapka`. Враќа колку уплати вкупно се променети.
     279
     280Прво ја прошируваме табелата `Uplata` со новиот статус и со колона за обележување дали казната е веќе зголемена:
     281{{{#!sql
     282ALTER TABLE Uplata DROP CONSTRAINT IF EXISTS uplata_status_check;
     283ALTER TABLE Uplata ADD CONSTRAINT uplata_status_check
     284    CHECK (status IN ('Plateno', 'Neplateno', 'Sudska_postapka'));
     285
     286ALTER TABLE Uplata ADD COLUMN IF NOT EXISTS kazna_zgolemena boolean DEFAULT false;
     287}}}
     288
     289{{{#!sql
     290CREATE OR REPLACE FUNCTION azuriraj_kazna_plakanja()
     291    RETURNS int
     292    LANGUAGE plpgsql
     293AS $$
     294DECLARE
     295    v_zgolemeni int := 0;
     296    v_na_sud    int := 0;
     297BEGIN
     298    UPDATE Uplata u
     299    SET iznos = u.iznos + (u.iznos * 0.50),
     300        kazna_zgolemena = true
     301    FROM Zapisnik z
     302    WHERE u.id_zapisnik = z.id_na_zapisnik
     303      AND u.status = 'Neplateno'
     304      AND u.kazna_zgolemena = false
     305      AND z.datum < CURRENT_DATE - INTERVAL '2 weeks';
     306    GET DIAGNOSTICS v_zgolemeni = ROW_COUNT;
     307
     308    UPDATE Uplata u
     309    SET status = 'Sudska_postapka'
     310    FROM Zapisnik z
     311    WHERE u.id_zapisnik = z.id_na_zapisnik
     312      AND u.status = 'Neplateno'
     313      AND z.datum < CURRENT_DATE - INTERVAL '2 months';
     314    GET DIAGNOSTICS v_na_sud = ROW_COUNT;
     315
     316    RAISE NOTICE 'Zgolemena kazna za % uplati, prefrleni % na sudska postapka.', v_zgolemeni, v_na_sud;
     317    RETURN v_zgolemeni + v_na_sud;
     318END;
     319$$;
     320}}}
     321
     322Повик:
     323{{{#!sql
     324SELECT azuriraj_kazna_plakanja();
     325SELECT status, COUNT(*) FROM Uplata GROUP BY status;
     326}}}