Version 3 (modified by 8 days ago) ( diff ) | ,
---|
Материјализирани погледи
1. Број казнети според линија секоја година
create materialized view kazni_po_linija_godisno as select extract(year from kz.kz_datum) as godina, li.li_ime, count(*) as broj_kazni from linija li join istanca_na_linija inl on inl.l_id = li.l_id join kontroli ko on ko.inl_id = inl.inl_id join kazna kz on kz.kontrola_id = ko.kontrola_id group by godina, li.li_ime order by godina, li.li_ime;
2. Најголем број возења по интервал од 1 час според линија за секоја година
create materialized view maks_vozenja_po_saat_godisno as select godina, li_ime, interval_1h, broj_vozenja from ( select extract(year from v.vozenje_start)::int as godina, li.li_ime, 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, count(*) as broj_vozenja from linija li join instanca_na_linija inl on inl.li_id = li.li_id join vozenje v on v.inl_id = inl.inl_id group by godina, li.li_ime, interval_1h ) as q1 where (godina, li_ime, broj_vozenja) in ( select godina, li_ime, max(broj_vozenja) from ( select extract(year from v.vozenje_start)::int as godina, li.li_ime, 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, count(*) as broj_vozenja from linija li join instanca_na_linija inl on inl.li_id = li.li_id join vozenje v on v.inl_id = inl.inl_id group by godina, li.li_ime, interval_1h ) as q2 group by godina, li_ime ) order by godina, li_ime;
3. Број возења од линија за секоја година
create materialized view vozenja_po_linija_godisno as select extract(year from v.vozenje_start) as godina, li.li_ime, li.li_pravec, count(v.vozenje_id) as broj_vozenja from linija li join instanca_na_linija inl on inl.li_id = li.li_id join vozenje v on v.inl_id = inl.inl_id group by godina, li.li_id, li.li_ime, li.li_pravec order by godina, li.li_id;
4. Број патници според постојка на дадена линија за секоја година
create materialized view patnici_po_postojka_godisno as select extract(year from v.vozenje_start) as godina, p.p_ime, li.li_ime, count(v.patnik_k_id) as broj_patnici from postojka p join postojka_na_linija pnl on pnl.p_id = p.p_id join vozenje v on v.pnl_id = pnl.pnl_id join patnik pa on pa.k_id = v.patnik_k_id join linija li on li.li_id = pnl.li_id group by godina, p.p_ime, li.li_ime order by godina, broj_patnici;
5. Часови во кои најмногу се купуваат часовни и дневни билети за секоја година
create materialized view maks_kupeni_bileti_po_cas_godisno as select tb_ime, godina, interval_1h, broj_bileti from ( select tb.tb_ime, extract(year from b.b_datum_na_kupuvanje)::int as godina, 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, count(*) as broj_bileti from tipbilet tb join bilet b on b.tb_id = tb.tb_id where tb.tb_ime in ('часовен', 'дневен') group by tb.tb_ime, godina, interval_1h ) q1 where (q1.tb_ime, q1.godina, q1.broj_bileti) in ( select tb_ime, godina, max(broj_bileti) from ( select tb.tb_ime, extract(year from b.b_datum_na_kupuvanje)::int as godina, 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, count(*) as broj_bileti from tipbilet tb join bilet b on b.tb_id = tb.tb_id where tb.tb_ime in ('часовен', 'дневен') group by tb.tb_ime, godina, interval_1h ) q2 group by godina, tb_ime ) order by q1.godina, q1.tb_ime;
6. Профит од казни и билети за секоја година
create materialized view profit_kazni_i_bileti_godisno as select godina, sum(coalesce(kz_iznos, 0)) + sum(coalesce(tb_cena, 0)) as vkupen_promet from ( select extract(year from kz.kz_datum)::int as godina, kz.kz_iznos, null::numeric as tb_cena from kazna kz where kz.kz_plateno = true and kz.kz_datum is not null and kz.kz_datum_plateno is not null and extract(year from kz.kz_datum) = extract(year from kz.kz_datum_plateno) union all select extract(year from b.b_datum_na_kupuvanje)::int as godina, null::numeric as kz_iznos, tb.tb_cena from bilet b join tipbilet tb on tb.tb_id = b.tb_id ) as subquery group by godina order by godina;
Индексирање
Прашалниците погоре служат за статистика и исвештаи преку годините.За истите за да не се извршуваат постојано резултатите од нив се складираат во материјализирани погледи.Ова го правиме за да се избегне прекумерно оптовораување на базата и исцрпување на ресурсите,имајќи во предвид дела податоците се на годишно ниво и обработката се извршива врз голем сет на податоци собрани за сите тие години.Иако веќе со ова се постигнува одредена оптимизација, за случаевите кога има потреба од обновување на податоците скалдирани во овие материјлизирани погледи може да воведеме индекси во веќе постоечките табели преку кои се доаѓа до овие материјализирани погледи за уште пооптимално извржување на прашалниците.Во прилог се индексите кои се воведуваат за секоја од табелите кој се дел од овие прашалници и причината за нивно воведување.
Индекси за Istanca na linija
табелата
create index idx_instancanalinija_li_id on instancanalinija (li_id);
Индекси за Kontroli
табелата
create index idx_kontroli_inl_id on kontroli (inl_id);
Индекси за Kazna
табелата
create index idx_kazna_kontrola_id on kazna (kontrola_id);
create index idx_kazna_kz_datum_year on kazna (extract(year from kz_datum));
Индекси за Vozenje
табелата
create index idx_vozenje_inl_id on vozenje(inl_id);
create index idx_vozenje_hour on vozenje(extract(year from vozenje_start));
Тригери и фукниции
1. Проверка на билет и ажурирање на статус на возење
create or replace function proverka_bilet_azuriranje_vozenja() returns trigger as $$ begin if exists ( select 1 from vozenje v where v.patnik_id = new.patnik_id and v.status = 'active' and v.vozenje_id != new.vozenje_id ) then update vozenje set end_date = new.start_date, status = 'finished' where patnik_id = new.patnik_id and status = 'active' and vozenje_id != new.vozenje_id; end if; if exists ( select 1 from bilet b join tipbilet tb on b.tb_id = tb.tb_id where b.b_id = new.b_id and b.status = 'active' and (b.datum_na_kupuvanje + (tb.tb_trajnost || ' seconds')::interval) < now() ) then update bilet set status = 'expired' where b_id = new.b_id; raise exception 'ticket % is expired', new.b_id; end if; return new; end; $$ language plpgsql; create or replace trigger menagiranje_vozenja before insert on vozenje for each row execute function proverka_bilet_azuriranje_vozenja();
create or replace function update_vozenje_on_line_instance_change() returns trigger as $$ begin -- update vozenje if inl_datum_end changed if new.inl_datum_end is distinct from old.inl_datum_end then update vozenje set end_date = new.inl_datum_end, status = 'finished' where inl_id = new.inl_id and status = 'active'; end if; return new; end; $$ language plpgsql; create or replace trigger update_vozenje_za_inl after update of inl_datum_end on instancanalinija for each row execute function update_vozenje_on_line_instance_change();
Процеудури и трансакции
1. Регистрација на корисник
create or replace procedure registracija_korisnik( i_k_ime varchar(4000), i_k_adresa varchar(4000), i_k_telefon varchar(500), i_k_email varchar(500), i_k_embg varchar(13), i_k_lozinka varchar(500), i_k_is_admin boolean, i_k_uloga varchar(500), i_v_plata float default null, i_v_datum_vrabotuvanje date default null, i_v_datum_prekin_vrabotuvanje date default null ) language plpgsql as $$ declare nov_k_id bigint; begin if i_k_ime is null or trim(i_k_ime) = '' then raise exception 'името е задолжително'; end if; if i_k_embg is null or length(i_k_embg) != 13 or i_k_embg !~ '^[0-9]{13}$' then raise exception 'ембг мора да има 13 карактери'; end if; if i_k_email is null or i_k_email !~ '^[a-za-z0-9._%+-]+@[a-za-z0-9.-]+\.[a-za-z]{2,}$' then raise exception 'невалидна е-маил адреса'; end if; if i_k_lozinka is null or trim(i_k_lozinka) = '' then raise exception 'лозинка е задолжителна'; end if; insert into korisnik( k_ime, k_adresa, k_telefon, k_email, k_embg, k_lozinka, k_is_admin ) values ( i_k_ime, i_k_adresa, i_k_telefon, i_k_email, i_k_embg, i_k_lozinka, i_k_is_admin ) returning k_id into nov_k_id; if i_k_uloga = 'patnik' then insert into patnik(k_id) values (nov_k_id); elsif i_k_uloga = 'vozac' then if i_v_plata is null or i_v_datum_vrabotuvanje is null then raise exception 'податоците за вработен се неопходни'; end if; if i_v_plata <= 0 then raise exception 'платата неможе да е нула или пониска вредност'; end if; insert into vraboten(k_id, v_plata, v_datum_na_vrabotuvanje, v_datum_prekin_vrabotuvanje) values (nov_k_id, i_v_plata, i_v_datum_vrabotuvanje, i_v_datum_prekin_vrabotuvanje); insert into vozac(k_id) values (nov_k_id); elsif i_k_uloga = 'konduktor' then if i_v_plata is null or i_v_datum_vrabotuvanje is null then raise exception 'податоците за вработен се неопходни'; end if; if i_v_plata <= 0 then raise exception 'платата неможе да е нула или пониска вредност'; end if; insert into vraboten(k_id, v_plata, v_datum_na_vrabotuvanje, v_datum_prekin_vrabotuvanje) values (nov_k_id, i_v_plata, i_v_datum_vrabotuvanje, i_v_datum_prekin_vrabotuvanje); insert into konduktor(k_id) values (nov_k_id); else raise exception 'невалидна улога: %', i_k_uloga; end if; exception when unique_violation then if sqlerrm ~ 'unizue_k_embg' then raise exception 'ембг-то % веќе постои', i_k_embg; elsif sqlerrm ~ 'unique_k_email' then raise exception 'е-маил-то % веќе постои', i_k_email; else raise exception 'грешка при регистрација: %', sqlerrm; end if; when others then raise exception 'грешка при регистрација: %', sqlerrm; end; $$;
2. Запишување казна
create or replace procedure zapisi_kazna( i_kz_iznos float, i_kz_plateno boolean, i_kz_datum timestamp, i_kz_datum_plateno timestamp default null, i_kz_dokument varchar(500), i_konduktor_k_id bigint, i_kontrola_id bigint, kz_tip varchar(500), i_patnik_k_id bigint default null, i_kzn_telefon varchar(500) default null, i_kzn_ime varchar(4000) default null, i_kzn_adresa varchar(4000) default null ) language plpgsql as $$ declare nova_kz_id bigint; begin if i_kz_iznos is null or i_kz_iznos <= 0 then raise exception 'износот на казната мора да биде позитивен'; end if; if i_kz_datum is null then raise exception 'датумот на казната е задолжителен'; end if; if i_kz_dokument is null or trim(i_kz_dokument) = '' then raise exception 'документот за казна е задолжителен'; end if; if i_konduktor_k_id is null or not exists ( select 1 from konduktor where k_id = i_konduktor_k_id ) then raise exception 'невалиден кондуктор'; end if; if i_kontrola_id is null or not exists ( select 1 from kontrola where kontrola_id = i_kontrola_id ) then raise exception 'невалидна контрола'; end if; insert into kazna( kz_iznos, kz_plateno, kz_datum, kz_datum_plateno, kz_dokument, konduktor_k_id, kontrola_id ) values ( i_kz_iznos, i_kz_plateno, i_kz_datum, i_kz_datum_plateno, i_kz_dokument, i_konduktor_k_id, i_kontrola_id ) returning kz_id into nova_kz_id; if kz_tip = 'registriran' then if i_patnik_k_id is null or not exists ( select 1 from patnik where k_id = i_patnik_k_id ) then raise exception 'грешка: не постои како регистриран патник'; end if; insert into kaznazaregistriran(kz_id, patnik_k_id) values (nova_kz_id, i_patnik_k_id); elsif kz_tip = 'neregistriran' then if i_kzn_ime is null or trim(i_kzn_ime) = '' then raise exception 'име е задолжително за нерегистриран патник'; end if; if i_kzn_adresa is null or trim(i_kzn_adresa) = '' then raise exception 'адреса е задолжителна за нерегистриран патник'; end if; if i_kzn_telefon is null or trim(i_kzn_telefon) = '' then raise exception 'телефонот е задолжителен за нерегистриран патник'; end if; if i_kzn_telefon !~ '^[0-9]{9,12}$' then raise exception 'невалиден формат на телефонски број'; end if; insert into kaznazaneregistriran( kz_id, kzn_telefon, kzn_ime, kzn_adresa ) values ( nova_kz_id, i_kzn_telefon, i_kzn_ime, i_kzn_adresa ); else raise exception 'патникот може да е само регистриран или нерегистриран: %', kz_tip; end if; exception when others then raise exception 'грешка при запишување казна: %', sqlerrm; end; $$;