Changes between Initial Version and Version 1 of AdvancedApplicationDesign_v2


Ignore:
Timestamp:
08/19/25 12:44:03 (2 days ago)
Author:
212028
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • AdvancedApplicationDesign_v2

    v1 v1  
     1= Материјализирани погледи и индексирање =
     2
     3=== 1. Број казнети според линија секоја година ===
     4
     5{{{#!div
     6{{{#!sql
     7create materialized view kazni_po_linija_godisno as
     8select
     9    extract(year from kz.kz_datum) as godina,
     10    li.li_ime,
     11    count(*) as broj_kazni
     12from linija li
     13join istanca_na_linija inl on inl.l_id = li.l_id
     14join kontroli ko on ko.inl_id = inl.inl_id
     15join kazna kz on kz.kontrola_id = ko.kontrola_id
     16group by godina, li.li_ime
     17order by godina, li.li_ime desc;
     18
     19create index idx_kazni_po_linija on kazni_po_linija_godisno(godina, li_ime);
     20}}}
     21}}}
     22
     23=== 2. Најголем број возења по интервал од 1 час според линија за секоја година ===
     24
     25{{{#!div
     26{{{#!sql
     27create materialized view maks_vozenja_po_saat_godisno as
     28select
     29    godina,
     30    li_ime,
     31    interval_1h,
     32    broj_vozenja
     33from (
     34    select
     35        extract(year from v.vozenje_start)::int as godina,
     36        li.li_ime,
     37        to_char(extract(hour from v.vozenje_start), '00') || ':00 - ' || to_char(extract(hour from v.vozenje_start + interval '1 hour'), '00') || ':00' as interval_1h,
     38        count(*) as broj_vozenja
     39    from linija li
     40    join instanca_na_linija inl on inl.li_id = li.li_id
     41    join vozenje v on v.inl_id = inl.inl_id
     42    group by godina, li.li_ime, interval_1h
     43) as q1
     44where (godina, li_ime, broj_vozenja) in (
     45    select
     46        godina,
     47        li_ime,
     48        max(broj_vozenja)
     49    from (
     50        select
     51            extract(year from v.vozenje_start)::int as godina,
     52            li.li_ime,
     53            to_char(extract(hour from v.vozenje_start), '00') || ':00 - ' || to_char(extract(hour from v.vozenje_start + interval '1 hour'), '00') || ':00' as interval_1h,
     54            count(*) as broj_vozenja
     55        from linija li
     56        join instanca_na_linija inl on inl.li_id = li.li_id
     57        join vozenje v on v.inl_id = inl.inl_id
     58        group by godina, li.li_ime, interval_1h
     59    ) as q2
     60    group by godina, li_ime
     61)
     62order by godina, li_ime;
     63
     64create index idx_vozenja_po_saat on maks_vozenja_po_saat_godisno (li_ime, godina, interval_1h);
     65}}}
     66}}}
     67
     68=== 3. Број возења од линија за секоја година ===
     69
     70{{{#!div
     71{{{#!sql
     72create materialized view vozenja_po_linija_godisno as
     73select
     74    extract(year from v.vozenje_start) as godina,
     75    li.li_ime,
     76    li.li_pravec,
     77    count(v.vozenje_id) as broj_vozenja
     78from linija li
     79join instanca_na_linija inl on inl.li_id = li.li_id
     80join vozenje v on v.inl_id = inl.inl_id
     81group by godina, li.li_id, li.li_ime, li.li_pravec
     82order by godina, li.li_id desc;
     83
     84create index idx_vozenja_po_linija on vozenja_po_linija_godisno (li_ime, godina);
     85}}}
     86}}}
     87
     88=== 4. Број патници според постојка на дадена линија за секоја година ===
     89
     90{{{#!div
     91{{{#!sql
     92create materialized view patnici_po_postojka_godisno as
     93select
     94    extract(year from v.vozenje_start) as godina,
     95    p.p_ime,
     96    li.li_ime,
     97    count(v.patnik_k_id) as broj_patnici
     98from postojka p
     99join postojka_na_linija pnl on pnl.p_id = p.p_id
     100join vozenje v on v.pnl_id = pnl.pnl_id
     101join patnik pa on pa.k_id = v.patnik_k_id
     102join linija li on li.li_id = pnl.li_id
     103group by godina, p.p_ime, li.li_ime
     104order by godina, broj_patnici desc;
     105
     106create index idx_patnici_po_postojka on patnici_po_postojka_godisno (godina, p_ime, li_ime);
     107}}}
     108}}}
     109
     110=== 5. Часови во кои најмногу се купуваат часовни и дневни билети за секоја година ===
     111
     112{{{#!div
     113{{{#!sql
     114create materialized view maks_kupeni_bileti_po_cas_godisno as
     115select
     116    tb_ime,
     117    godina,
     118    interval_1h,
     119    broj_bileti
     120from (
     121    select
     122        tb.tb_ime,
     123        extract(year from b.b_datum_na_kupuvanje)::int as godina,
     124        to_char(extract(hour from b.b_datum_na_kupuvanje), '00') || ':00 - ' || to_char((extract(hour from b.b_datum_na_kupuvanje) + 1)::int, '00') || ':00' as interval_1h,
     125        count(*) as broj_bileti
     126    from tipbilet tb
     127    join bilet b on b.tb_id = tb.tb_id
     128    where tb.tb_ime in ('часовен', 'дневен')
     129    group by tb.tb_ime, godina, interval_1h
     130) q1
     131where (q1.tb_ime, q1.godina, q1.broj_bileti) in (
     132    select
     133        tb_ime,
     134        godina,
     135        max(broj_bileti)
     136    from (
     137        select
     138            tb.tb_ime,
     139            extract(year from b.b_datum_na_kupuvanje)::int as godina,
     140            to_char(extract(hour from b.b_datum_na_kupuvanje), '00') || ':00 - ' || to_char((extract(hour from b.b_datum_na_kupuvanje) + 1)::int, '00') || ':00' as interval_1h,
     141            count(*) as broj_bileti
     142        from tipbilet tb
     143        join bilet b on b.tb_id = tb.tb_id
     144        where tb.tb_ime in ('часовен', 'дневен')
     145        group by tb.tb_ime, godina, interval_1h
     146    ) q2
     147    group by godina, tb_ime
     148)
     149order by q1.godina, q1.tb_ime;
     150
     151create index idx_bileti_kupeni_od_cas on maks_kupeni_bileti_po_cas_godisno (tb_ime, godina, interval_1h);
     152}}}
     153}}}
     154
     155=== 6. Профит од казни и билети за секоја година ===
     156
     157{{{#!div
     158{{{#!sql
     159create materialized view profit_kazni_i_bileti_godisno as
     160select
     161    godina,
     162    sum(coalesce(kz_iznos, 0)) + sum(coalesce(tb_cena, 0)) as vkupen_promet
     163from (
     164    select
     165        extract(year from kz.kz_datum)::int as godina,
     166        kz.kz_iznos,
     167        null::numeric as tb_cena
     168    from kazna kz
     169    where
     170        kz.kz_plateno = true
     171        and kz.kz_datum is not null
     172        and kz.kz_datum_plateno is not null
     173        and extract(year from kz.kz_datum) = extract(year from kz.kz_datum_plateno)
     174    union all
     175    select
     176        extract(year from b.b_datum_na_kupuvanje)::int as godina,
     177        null::numeric as kz_iznos,
     178        tb.tb_cena
     179    from bilet b
     180    join tipbilet tb on tb.tb_id = b.tb_id
     181) as subquery
     182group by godina
     183order by godina;
     184
     185create index idx_prihod_godisen on profit_kazni_i_bileti_godisno(godina);
     186}}}
     187}}}
     188
     189= Тригери и фукниции =
     190
     191=== 1. Промена на статус на билет ===
     192
     193{{{#!div
     194{{{#!sql
     195create or replace function bilet_status()
     196returns trigger as
     197$$
     198begin
     199    if new.b_status = 'expired' then
     200        raise exception 'билетот е веќе истечен';
     201    end if;
     202
     203    if new.b_status = 'inactive' and new.b_datum_aktivacija is null then
     204        new.b_datum_aktivacija = now();
     205        new.b_status = 'active';
     206    end if;
     207
     208    if new.b_status = 'active' and new.b_datum_aktivacija is not null then
     209        if exists (
     210            select 1
     211            from tipbilet tb
     212            where tb.tb_id = new.tb_id
     213                and (new.b_datum_aktivacija + (tb.tb_trajnost || ' minutes')::interval) < now()
     214        ) then
     215            new.b_status = 'expired';
     216            raise exception 'билетот е истечен';
     217        end if;
     218    end if;
     219
     220    return new;
     221end;
     222$$ language plpgsql;
     223
     224create or replace trigger proverka_bilet
     225before insert or update on bilet
     226for each row
     227execute function bilet_status();
     228}}}
     229}}}
     230
     231=== 2. Промена на статус на билет ===
     232
     233{{{#!div
     234{{{#!sql
     235create or replace function vozenje_status()
     236returns trigger as $$
     237begin
     238    update vozenje
     239    set vozenje_end = now(), vozenje_status = 'finished'
     240    where patnik_k_id = new.patnik_k_id
     241        and vozenje_start is not null
     242        and vozenje_end is null
     243        and vozenje_status = 'active'
     244        and vozenje_id != new.vozenje_id;
     245
     246    if new.vozenje_end is not null and new.vozenje_status != 'finished' then
     247        new.vozenje_status = 'finished';
     248    end if;
     249
     250    if new.vozenje_end is null then
     251        new.vozenje_status = 'active';
     252    end if;
     253
     254    return new;
     255end;
     256$$ language plpgsql;
     257
     258create or replace trigger vozenje_proverka
     259before insert or update on vozenje
     260for each row
     261execute function vozenje_status();
     262}}}
     263}}}
     264
     265= Процеудури и трансакции =
     266
     267==== 1. Регистрација на корисник ====
     268
     269{{{#!div
     270{{{#!sql
     271create or replace procedure registracija_korisnik(
     272    i_k_ime varchar(4000),
     273    i_k_adresa varchar(4000),
     274    i_k_telefon varchar(500),
     275    i_k_email varchar(500),
     276    i_k_embg varchar(13),
     277    i_k_lozinka varchar(500),
     278    i_k_is_admin boolean,
     279    i_k_uloga varchar(500),
     280    i_v_plata float default null,
     281    i_v_datum_vrabotuvanje date default null,
     282    i_v_datum_prekin_vrabotuvanje date default null
     283)
     284language plpgsql
     285as $$
     286declare
     287    nov_k_id bigint;
     288begin
     289    if i_k_ime is null or trim(i_k_ime) = '' then
     290        raise exception 'името е задолжително';
     291    end if;
     292
     293    if i_k_embg is null or length(i_k_embg) != 13 or i_k_embg !~ '^[0-9]{13}$' then
     294        raise exception 'ембг мора да има 13 карактери';
     295    end if;
     296
     297    if i_k_email is null or i_k_email !~ '^[a-za-z0-9._%+-]+@[a-za-z0-9.-]+\.[a-za-z]{2,}$' then
     298        raise exception 'невалидна е-маил адреса';
     299    end if;
     300
     301    if i_k_lozinka is null or trim(i_k_lozinka) = '' then
     302        raise exception 'лозинка е задолжителна';
     303    end if;
     304
     305    insert into korisnik(
     306        k_ime, k_adresa, k_telefon, k_email, k_embg, k_lozinka, k_is_admin
     307    ) values (
     308        i_k_ime, i_k_adresa, i_k_telefon, i_k_email, i_k_embg, i_k_lozinka, i_k_is_admin
     309    ) returning k_id into nov_k_id;
     310
     311    if i_k_uloga = 'patnik' then
     312        insert into patnik(k_id) values (nov_k_id);
     313    elsif i_k_uloga = 'vozac' then
     314        if i_v_plata is null or i_v_datum_vrabotuvanje is null then
     315            raise exception 'податоците за вработен се неопходни';
     316        end if;
     317        if i_v_plata <= 0 then
     318            raise exception 'платата неможе да е нула или пониска вредност';
     319        end if;
     320        insert into vraboten(k_id, v_plata, v_datum_na_vrabotuvanje, v_datum_prekin_vrabotuvanje)
     321        values (nov_k_id, i_v_plata, i_v_datum_vrabotuvanje, i_v_datum_prekin_vrabotuvanje);
     322        insert into vozac(k_id) values (nov_k_id);
     323    elsif i_k_uloga = 'konduktor' then
     324        if i_v_plata is null or i_v_datum_vrabotuvanje is null then
     325            raise exception 'податоците за вработен се неопходни';
     326        end if;
     327        if i_v_plata <= 0 then
     328            raise exception 'платата неможе да е нула или пониска вредност';
     329        end if;
     330        insert into vraboten(k_id, v_plata, v_datum_na_vrabotuvanje, v_datum_prekin_vrabotuvanje)
     331        values (nov_k_id, i_v_plata, i_v_datum_vrabotuvanje, i_v_datum_prekin_vrabotuvanje);
     332        insert into konduktor(k_id) values (nov_k_id);
     333    else
     334        raise exception 'невалидна улога: %', i_k_uloga;
     335    end if;
     336exception
     337    when unique_violation then
     338        if sqlerrm ~ 'unizue_k_embg' then
     339            raise exception 'ембг-то % веќе постои', i_k_embg;
     340        elsif sqlerrm ~ 'unique_k_email' then
     341            raise exception 'е-маил-то % веќе постои', i_k_email;
     342        else
     343            raise exception 'грешка при регистрација: %', sqlerrm;
     344        end if;
     345    when others then
     346        raise exception 'грешка при регистрација: %', sqlerrm;
     347end;
     348$$;
     349}}}
     350}}}
     351
     352==== 2. Запишување казна ====
     353
     354{{{#!div
     355{{{#!sql
     356create or replace procedure zapisi_kazna(
     357    i_kz_iznos float,
     358    i_kz_plateno boolean,
     359    i_kz_datum timestamp,
     360    i_kz_datum_plateno timestamp default null,
     361    i_kz_dokument varchar(500),
     362    i_konduktor_k_id bigint,
     363    i_kontrola_id bigint,
     364    kz_tip varchar(500),
     365    i_patnik_k_id bigint default null,
     366    i_kzn_telefon varchar(500) default null,
     367    i_kzn_ime varchar(4000) default null,
     368    i_kzn_adresa varchar(4000) default null
     369)
     370language plpgsql
     371as $$
     372declare
     373    nova_kz_id bigint;
     374begin
     375    if i_kz_iznos is null or i_kz_iznos <= 0 then
     376        raise exception 'износот на казната мора да биде позитивен';
     377    end if;
     378
     379    if i_kz_datum is null then
     380        raise exception 'датумот на казната е задолжителен';
     381    end if;
     382
     383    if i_kz_dokument is null or trim(i_kz_dokument) = '' then
     384        raise exception 'документот за казна е задолжителен';
     385    end if;
     386
     387    if i_konduktor_k_id is null or not exists (
     388        select 1
     389        from konduktor
     390        where k_id = i_konduktor_k_id
     391    ) then
     392        raise exception 'невалиден кондуктор';
     393    end if;
     394
     395    if i_kontrola_id is null or not exists (
     396        select 1
     397        from kontrola
     398        where kontrola_id = i_kontrola_id
     399    ) then
     400        raise exception 'невалидна контрола';
     401    end if;
     402
     403    insert into kazna(
     404        kz_iznos, kz_plateno, kz_datum, kz_datum_plateno, kz_dokument, konduktor_k_id, kontrola_id
     405    ) values (
     406        i_kz_iznos, i_kz_plateno, i_kz_datum, i_kz_datum_plateno, i_kz_dokument, i_konduktor_k_id, i_kontrola_id
     407    ) returning kz_id into nova_kz_id;
     408
     409    if kz_tip = 'registriran' then
     410        if i_patnik_k_id is null or not exists (
     411            select 1
     412            from patnik
     413            where k_id = i_patnik_k_id
     414        ) then
     415            raise exception 'грешка: не постои како регистриран патник';
     416        end if;
     417        insert into kaznazaregistriran(kz_id, patnik_k_id)
     418        values (nova_kz_id, i_patnik_k_id);
     419    elsif kz_tip = 'neregistriran' then
     420        if i_kzn_ime is null or trim(i_kzn_ime) = '' then
     421            raise exception 'име е задолжително за нерегистриран патник';
     422        end if;
     423        if i_kzn_adresa is null or trim(i_kzn_adresa) = '' then
     424            raise exception 'адреса е задолжителна за нерегистриран патник';
     425        end if;
     426        if i_kzn_telefon is null or trim(i_kzn_telefon) = '' then
     427            raise exception 'телефонот е задолжителен за нерегистриран патник';
     428        end if;
     429        if i_kzn_telefon !~ '^[0-9]{9,12}$' then
     430            raise exception 'невалиден формат на телефонски број';
     431        end if;
     432        insert into kaznazaneregistriran(
     433            kz_id, kzn_telefon, kzn_ime, kzn_adresa
     434        ) values (
     435            nova_kz_id, i_kzn_telefon, i_kzn_ime, i_kzn_adresa
     436        );
     437    else
     438        raise exception 'патникот може да е само регистриран или нерегистриран: %', kz_tip;
     439    end if;
     440exception
     441    when others then
     442        raise exception 'грешка при запишување казна: %', sqlerrm;
     443end;
     444$$;
     445}}}
     446}}}