wiki:DatabaseProgramming

Version 7 (modified by 231018, 6 hours ago) ( diff )

--

Функции, Процедури и Тригери

Во оваа фаза беа имплементирани функции, процедури и тригери кои ја поддржуваат основната бизнис логика на системот Safe City Security. Тие овозможуваат автоматизација на процеси како проверка на статус на казна, пресметка на износ, евидентирање плаќање, поднесување жалба, архивирање снимки и автоматско ажурирање на податоци преку тригери.

phase04.sql

Функции

Функциите се користат за пресметување и враќање на вредности кои се потребни во повеќе делови од системот.

1. fn_status_kazna

CREATE OR REPLACE FUNCTION fn_status_kazna(p_kazna_id INTEGER)
RETURNS VARCHAR
AS $$
DECLARE
    v_status VARCHAR;
BEGIN
    SELECT sk.ime
    INTO v_status
    FROM Kazna k
    JOIN StatusKazna sk ON sk.status_kazna_id = k.status_kazna_id
    WHERE k.kazna_id = p_kazna_id;

    IF v_status IS NULL THEN
        RETURN 'Kaznata ne postoi';
    ELSIF LOWER(v_status) = 'platena' THEN
        RETURN 'Kaznata e platena';
    ELSIF LOWER(v_status) = 'neplatena' THEN
        RETURN 'Kaznata ne e platena';
    ELSIF LOWER(v_status) = 'ponishtena' THEN
        RETURN 'Kaznata e ponishtena';
    ELSE
        RETURN 'Nepoznat status na kazna';
    END IF;
END;
$$
LANGUAGE plpgsql;

Функцијата fn_status_kazna го враќа статусот на одредена казна според нејзиниот идентификатор. Како влезен параметар прима p_kazna_id, а потоа од табелата Kazna и поврзаната табела StatusKazna го зема името на статусот на казната. Ако казната не постои, функцијата враќа Kaznata ne postoi. Ако статусот е platena, враќа Kaznata e platena. Ако статусот е neplatena, враќа Kaznata ne e platena. Ако статусот е ponishtena, враќа Kaznata e ponishtena, а во останатите случаи враќа Nepoznat status na kazna.

Оваа функција е корисна кога системот треба брзо да прикаже дали одредена казна е платена, неплатена или поништена, без секогаш рачно да се проверува статусот преку табелите Kazna и StatusKazna.

2. fn_presmetaj_iznos_kazna

CREATE OR REPLACE FUNCTION fn_presmetaj_iznos_kazna(p_tip_prekrsok_id INTEGER)
RETURNS INTEGER
AS $$
DECLARE
    v_iznos INTEGER;
BEGIN
    SELECT iznos
    INTO v_iznos
    FROM TipPrekrsok
    WHERE tip_prekrsok_id = p_tip_prekrsok_id;

    IF v_iznos IS NULL THEN
        RAISE EXCEPTION 'Ne postoi tip na prekrsok so id %', p_tip_prekrsok_id;
    ELSIF v_iznos <= 0 THEN
        RAISE EXCEPTION 'Iznosot na kaznata mora da bide pogolem od 0';
    ELSE
        RETURN v_iznos;
    END IF;
END;
$$
LANGUAGE plpgsql;

Функцијата fn_presmetaj_iznos_kazna се користи за пресметување на износот на казната според типот на прекршок. Како параметар прима tip_prekrsok_id, а потоа од табелата TipPrekrsok го зема соодветниот износ.

Оваа функција се користи во процедурата за креирање прекршок со казна, каде што прво се пресметува износот, а потоа автоматски се креира запис во табелата Kazna.

Процедури

Процедурите се користат за извршување операции врз базата, како внесување, ажурирање и архивирање податоци.

1. sp_evidentiraj_plakanje

CREATE OR REPLACE PROCEDURE sp_evidentiraj_plakanje(
    p_kazna_id INTEGER,
    p_metod_plakanje_id INTEGER
)
AS $$
DECLARE
    v_postoi_kazna INTEGER;
    v_postoi_metod INTEGER;
    v_status VARCHAR;
BEGIN
    SELECT COUNT(*)
    INTO v_postoi_kazna
    FROM Kazna
    WHERE kazna_id = p_kazna_id;

    IF v_postoi_kazna = 0 THEN
        RAISE EXCEPTION 'Ne postoi kazna so id %', p_kazna_id;
    END IF;


    SELECT COUNT(*)
    INTO v_postoi_metod
    FROM MetodPlakanje
    WHERE metod_plakanje_id = p_metod_plakanje_id;

    IF v_postoi_metod = 0 THEN
        RAISE EXCEPTION 'Ne postoi metod na plakanje so id %', p_metod_plakanje_id;
    END IF;


    SELECT sk.ime
    INTO v_status
    FROM Kazna k
    JOIN StatusKazna sk ON sk.status_kazna_id = k.status_kazna_id
    WHERE k.kazna_id = p_kazna_id;


    IF LOWER(v_status) = 'platena' THEN
        RAISE EXCEPTION 'Kaznata e vekje platena';
    ELSIF LOWER(v_status) = 'ponishtena' THEN
        RAISE EXCEPTION 'Ponisthena kazna ne moze da se plati';
    ELSE
        INSERT INTO Plakanje(
            metod_plakanje_id,
            datum,
            kazna_id
        )
        VALUES(
            p_metod_plakanje_id,
            CURRENT_DATE,
            p_kazna_id
        );
    END IF;
END;
$$
LANGUAGE plpgsql;

Процедурата sp_evidentiraj_plakanje служи за евидентирање плаќање на казна. Како влезни параметри прима p_kazna_id, односно идентификатор на казната, и p_metod_plakanje_id, односно идентификатор на методот на плаќање. Најпрво процедурата проверува дали постои казна со дадениот идентификатор во табелата Kazna. Доколку не постои, се прикажува грешка. Потоа проверува дали постои метод на плаќање со дадениот идентификатор во табелата MetodPlakanje.

Со помош на IF / ELSIF / ELSE проверки, процедурата го зема моменталниот статус на казната преку табелата StatusKazna. Ако казната е веќе platena, се прикажува грешка дека казната е веќе платена. Ако казната е ponishtena, се прикажува грешка дека поништена казна не може да се плати. Во спротивно, ако казната може да се плати, се креира нов запис во табелата Plakanje, каде што се зачувуваат методот на плаќање, тековниот датум и идентификаторот на казната.

Оваа процедура е корисна бидејќи ја автоматизира операцијата за плаќање казна и спречува нелогични ситуации, како повторно плаќање на веќе платена казна или плаќање на поништена казна.

2. sp_podnesi_zalba

CREATE OR REPLACE PROCEDURE sp_podnesi_zalba(
    p_sodrzina VARCHAR,
    p_korisnik_id INTEGER,
    p_prekrsok_id INTEGER
)
AS $$
DECLARE
    v_status_podnesena INTEGER;
    v_postoi_korisnik INTEGER;
    v_postoi_prekrsok INTEGER;
BEGIN
    IF p_sodrzina IS NULL OR LENGTH(TRIM(p_sodrzina)) = 0 THEN
        RAISE EXCEPTION 'Zalbata mora da ima sodrzina';
    END IF;


    SELECT COUNT(*)
    INTO v_postoi_korisnik
    FROM Korisnik
    WHERE korisnik_id = p_korisnik_id;

    IF v_postoi_korisnik = 0 THEN
        RAISE EXCEPTION 'Ne postoi korisnik so id %', p_korisnik_id;
    END IF;


    SELECT COUNT(*)
    INTO v_postoi_prekrsok
    FROM Prekrsok
    WHERE prekrsok_id = p_prekrsok_id;

    IF v_postoi_prekrsok = 0 THEN
        RAISE EXCEPTION 'Ne postoi prekrsok so id %', p_prekrsok_id;
    END IF;


    SELECT status_zalba_id
    INTO v_status_podnesena
    FROM StatusZalba
    WHERE LOWER(ime) = 'podnesena';

    IF v_status_podnesena IS NULL THEN
        RAISE EXCEPTION 'Vo StatusZalba ne postoi status podnesena';
    ELSE
        INSERT INTO Zalba(
            sodrzina,
            datum_na_podnesuvanje,
            status_zalba_id,
            korisnik_id,
            prekrsok_id
        )
        VALUES(
            p_sodrzina,
            CURRENT_DATE,
            v_status_podnesena,
            p_korisnik_id,
            p_prekrsok_id
        );
    END IF;
END;
$$
LANGUAGE plpgsql;

Процедурата sp_podnesi_zalba се користи за поднесување жалба од страна на корисник за одреден прекршок. Како параметри прима p_sodrzina, p_korisnik_id и p_prekrsok_id.

Процедурата проверува дали жалбата има содржина, дали постои корисникот во табелата Korisnik и дали постои прекршокот во табелата Prekrsok. Потоа од табелата StatusZalba го зема статусот podnesena, кој се поставува како почетен статус на новата жалба.

Ако сите проверки се успешни, се креира нов запис во табелата Zalba, со содржина, тековен датум, статус, корисник и прекршок. Ова овозможува жалбата правилно да биде поврзана со конкретен корисник и конкретен прекршок.

3. sp_razgledaj_zalba

CREATE OR REPLACE PROCEDURE sp_razgledaj_zalba(
    p_zalba_id INTEGER,
    p_administrator_id INTEGER,
    p_nov_status VARCHAR
)
AS $$
DECLARE
    v_status_id INTEGER;
    v_postoi_zalba INTEGER;
    v_postoi_admin INTEGER;
BEGIN
    SELECT COUNT(*)
    INTO v_postoi_zalba
    FROM Zalba
    WHERE zalba_id = p_zalba_id;

    IF v_postoi_zalba = 0 THEN
        RAISE EXCEPTION 'Ne postoi zalba so id %', p_zalba_id;
    END IF;


    SELECT COUNT(*)
    INTO v_postoi_admin
    FROM Administrator
    WHERE administrator_id = p_administrator_id;

    IF v_postoi_admin = 0 THEN
        RAISE EXCEPTION 'Ne postoi administrator so id %', p_administrator_id;
    END IF;


    SELECT status_zalba_id
    INTO v_status_id
    FROM StatusZalba
    WHERE LOWER(ime) = LOWER(p_nov_status);

    IF v_status_id IS NULL THEN
        RAISE EXCEPTION 'Ne postoi status na zalba so ime %', p_nov_status;
    ELSE
        UPDATE Zalba
        SET administrator_id = p_administrator_id,
            status_zalba_id = v_status_id
        WHERE zalba_id = p_zalba_id;
    END IF;
END;
$$
LANGUAGE plpgsql;

Процедурата sp_razgledaj_zalba се користи кога администратор разгледува жалба. Таа го ажурира администраторот кој ја обработува жалбата и го менува нејзиниот статус.

Оваа процедура ја претставува логиката за управување со жалби во системот, односно премин од поднесена жалба кон понатамошна обработка.

4. sp_arhiviraj_stari_snimki

CREATE OR REPLACE PROCEDURE sp_arhiviraj_stari_snimki()
AS $$
DECLARE
    v_broj_snimki INTEGER;
BEGIN
    SELECT COUNT(*)
    INTO v_broj_snimki
    FROM Snimka
    WHERE datum < CURRENT_DATE - INTERVAL '365 days'
      AND arhivirana = FALSE;

    IF v_broj_snimki = 0 THEN
        RAISE NOTICE 'Nema snimki za arhiviranje';
    ELSE
        UPDATE Snimka
        SET arhivirana = TRUE,
            datum_arhiviranje = CURRENT_DATE
        WHERE datum < CURRENT_DATE - INTERVAL '365 days'
          AND arhivirana = FALSE;

        RAISE NOTICE 'Arhivirani se % snimki', v_broj_snimki;
    END IF;
END;
$$
LANGUAGE plpgsql;

Процедурата sp_arhiviraj_stari_snimki ги архивира старите снимки кои се постари од една година. Таа ги ажурира записите во табелата Snimka, поставува arhivirana = TRUE и внесува датум на архивирање.

Оваа процедура е корисна за одржување на базата, бидејќи старите снимки не мора постојано да се третираат како активни податоци.

5. sp_kreiraj_prekrsok_so_kazna

CREATE OR REPLACE PROCEDURE sp_kreiraj_prekrsok_so_kazna(
    p_opis VARCHAR,
    p_vreme TIME,
    p_datum DATE,
    p_detektirana_brzina INTEGER,
    p_tip_prekrsok_id INTEGER,
    p_kamera_id INTEGER,
    p_storitel_embg VARCHAR
)
AS $$
DECLARE
    v_kazna_id INTEGER;
    v_prekrsok_id INTEGER;
    v_iznos INTEGER;
    v_status_kazna_id INTEGER;
    v_status_prekrsok_id INTEGER;
    v_postoi_tip INTEGER;
    v_postoi_kamera INTEGER;
    v_postoi_gragjanin INTEGER;
BEGIN
    IF p_opis IS NULL OR LENGTH(TRIM(p_opis)) = 0 THEN
        RAISE EXCEPTION 'Opisot na prekrsokot ne smee da bide prazen';
    END IF;


    IF p_datum IS NULL THEN
        p_datum := CURRENT_DATE;
    END IF;


    IF p_detektirana_brzina IS NULL OR p_detektirana_brzina <= 0 THEN
        RAISE EXCEPTION 'Detektiranata brzina mora da bide pogolema od 0';
    END IF;


    IF p_storitel_embg IS NULL OR LENGTH(TRIM(p_storitel_embg)) <> 13 THEN
        RAISE EXCEPTION 'EMBG na storitelot mora da ima 13 karakteri';
    END IF;


    SELECT COUNT(*)
    INTO v_postoi_gragjanin
    FROM Gragjanin
    WHERE embg = p_storitel_embg;

    IF v_postoi_gragjanin = 0 THEN
        RAISE EXCEPTION 'Ne postoi gragjanin so EMBG %', p_storitel_embg;
    END IF;


    SELECT COUNT(*)
    INTO v_postoi_tip
    FROM TipPrekrsok
    WHERE tip_prekrsok_id = p_tip_prekrsok_id;

    IF v_postoi_tip = 0 THEN
        RAISE EXCEPTION 'Ne postoi tip prekrsok so id %', p_tip_prekrsok_id;
    END IF;


    SELECT COUNT(*)
    INTO v_postoi_kamera
    FROM Kamera
    WHERE kamera_id = p_kamera_id;

    IF v_postoi_kamera = 0 THEN
        RAISE EXCEPTION 'Ne postoi kamera so id %', p_kamera_id;
    END IF;


    SELECT status_kazna_id
    INTO v_status_kazna_id
    FROM StatusKazna
    WHERE LOWER(ime) = 'neplatena';

    IF v_status_kazna_id IS NULL THEN
        RAISE EXCEPTION 'Vo StatusKazna ne postoi status neplatena';
    END IF;


    SELECT status_prekrsok_id
    INTO v_status_prekrsok_id
    FROM StatusPrekrsok
    WHERE LOWER(ime) = 'aktiven';

    IF v_status_prekrsok_id IS NULL THEN
        RAISE EXCEPTION 'Vo StatusPrekrsok ne postoi status aktiven';
    END IF;


    v_iznos := fn_presmetaj_iznos_kazna(p_tip_prekrsok_id);


    INSERT INTO Kazna(
        datum,
        status_kazna_id,
        iznos_za_plakanje
    )
    VALUES(
        CURRENT_DATE,
        v_status_kazna_id,
        v_iznos
    )
    RETURNING kazna_id INTO v_kazna_id;


    INSERT INTO Prekrsok(
        opis,
        vreme,
        status_prekrsok_id,
        datum,
        detektirana_brzina,
        tip_prekrsok_id,
        kamera_id,
        kazna_id
    )
    VALUES(
        p_opis,
        p_vreme,
        v_status_prekrsok_id,
        p_datum,
        p_detektirana_brzina,
        p_tip_prekrsok_id,
        p_kamera_id,
        v_kazna_id
    )
    RETURNING prekrsok_id INTO v_prekrsok_id;


    INSERT INTO Prekrsok_Storitel(
        prekrsok_id,
        storitel_embg
    )
    VALUES(
        v_prekrsok_id,
        p_storitel_embg
    );
END;
$$
LANGUAGE plpgsql;

Процедурата sp_kreiraj_prekrsok_so_kazna автоматски креира прекршок заедно со соодветна казна. Најпрво се повикува функцијата fn_presmetaj_iznos_kazna, со која се пресметува износот на казната според типот на прекршок. Потоа се внесува нов запис во табелата Kazna, а добиениот kazna_id се користи при внесување на нов прекршок во табелата Prekrsok.

Оваа процедура е важна бидејќи поврзува две операции во една целина: креирање казна и креирање прекршок.

Тригери

Тригерите се користат за автоматско извршување логика при внесување или промена на податоци во табелите.

1. trg_auto_rok_kazna

CREATE OR REPLACE FUNCTION trg_fn_auto_rok_kazna()
RETURNS TRIGGER
AS $$
BEGIN
    IF NEW.datum IS NULL THEN
        NEW.datum := CURRENT_DATE;
    END IF;


    IF NEW.rok_na_plakanje IS NULL THEN
        NEW.rok_na_plakanje := NEW.datum + INTERVAL '30 days';
    ELSIF NEW.rok_na_plakanje < NEW.datum THEN
        RAISE EXCEPTION 'Rokot za plakanje ne moze da bide pred datumot na kaznata';
    ELSE
        RETURN NEW;
    END IF;

    RETURN NEW;
END;
$$
LANGUAGE plpgsql;

Тригерот trg_auto_rok_kazna се активира пред внесување нова казна во табелата Kazna. Ако не е внесен датум, автоматски се поставува тековниот датум. Ако не е внесен рок за плаќање, тој автоматски се пресметува како 30 дена по датумот на казната.

Со ова се избегнува рачно внесување на рок за секоја казна и се обезбедува конзистентност во системот.

2. trg_kazna_platena

CREATE OR REPLACE FUNCTION trg_fn_kazna_platena()
RETURNS TRIGGER
AS $$
DECLARE
    v_status_platena_id INTEGER;
    v_status_momentalen VARCHAR;
BEGIN
    SELECT sk.ime
    INTO v_status_momentalen
    FROM Kazna k
    JOIN StatusKazna sk ON sk.status_kazna_id = k.status_kazna_id
    WHERE k.kazna_id = NEW.kazna_id;


    IF v_status_momentalen IS NULL THEN
        RAISE EXCEPTION 'Kaznata ne postoi';
    ELSIF LOWER(v_status_momentalen) = 'ponishtena' THEN
        RAISE EXCEPTION 'Ponisthena kazna ne moze da se oznaci kako platena';
    ELSE
        SELECT status_kazna_id
        INTO v_status_platena_id
        FROM StatusKazna
        WHERE LOWER(ime) = 'platena';

        IF v_status_platena_id IS NULL THEN
            RAISE EXCEPTION 'Vo StatusKazna ne postoi status platena';
        ELSE
            UPDATE Kazna
            SET status_kazna_id = v_status_platena_id
            WHERE kazna_id = NEW.kazna_id;
        END IF;
    END IF;

    RETURN NEW;
END;
$$
LANGUAGE plpgsql;

Тригерот trg_kazna_platena се активира по внесување запис во табелата Plakanje. Кога ќе се евидентира плаќање, тригерот автоматски го ажурира статусот на соодветната казна во табелата Kazna и го поставува на 1, што означува дека казната е платена.

Ова овозможува автоматско поврзување меѓу плаќањето и статусот на казната.

3. trg_validacija_zalba

CREATE OR REPLACE FUNCTION trg_fn_validacija_zalba()
RETURNS TRIGGER
AS $$
DECLARE
    v_status_prekrsok VARCHAR;
BEGIN
    IF NEW.sodrzina IS NULL OR LENGTH(TRIM(NEW.sodrzina)) = 0 THEN
        RAISE EXCEPTION 'Zalbata mora da ima sodrzina';
    END IF;


    IF NEW.datum_na_podnesuvanje IS NULL THEN
        NEW.datum_na_podnesuvanje := CURRENT_DATE;
    END IF;


    SELECT sp.ime
    INTO v_status_prekrsok
    FROM Prekrsok p
    JOIN StatusPrekrsok sp ON sp.status_prekrsok_id = p.status_prekrsok_id
    WHERE p.prekrsok_id = NEW.prekrsok_id;


    IF v_status_prekrsok IS NULL THEN
        RAISE EXCEPTION 'Ne postoi prekrsok za koj se podnesuva zalbata';
    ELSIF LOWER(v_status_prekrsok) = 'ponishten' THEN
        RAISE EXCEPTION 'Ne moze da se podnese zalba za ponishten prekrsok';
    ELSE
        RETURN NEW;
    END IF;
END;
$$
LANGUAGE plpgsql;

Тригерот trg_validacija_zalba се активира пред внесување нова жалба во табелата Zalba. Тој проверува дали содржината на жалбата е празна или NULL. Ако нема содржина, тригерот фрла грешка и не дозволува внесување на невалидна жалба.

Овој тригер служи како дополнителна заштита за квалитетот на податоците во базата.

Attachments (1)

Download all attachments as: .zip

Note: See TracWiki for help on using the wiki.