| | 1 | = Материјализирани погледи и индексирање = |
| | 2 | |
| | 3 | === 1. Број казнети според линија секоја година === |
| | 4 | |
| | 5 | {{{#!div |
| | 6 | {{{#!sql |
| | 7 | create materialized view kazni_po_linija_godisno as |
| | 8 | select |
| | 9 | extract(year from kz.kz_datum) as godina, |
| | 10 | li.li_ime, |
| | 11 | count(*) as broj_kazni |
| | 12 | from linija li |
| | 13 | join istanca_na_linija inl on inl.l_id = li.l_id |
| | 14 | join kontroli ko on ko.inl_id = inl.inl_id |
| | 15 | join kazna kz on kz.kontrola_id = ko.kontrola_id |
| | 16 | group by godina, li.li_ime |
| | 17 | order by godina, li.li_ime desc; |
| | 18 | |
| | 19 | create index idx_kazni_po_linija on kazni_po_linija_godisno(godina, li_ime); |
| | 20 | }}} |
| | 21 | }}} |
| | 22 | |
| | 23 | === 2. Најголем број возења по интервал од 1 час според линија за секоја година === |
| | 24 | |
| | 25 | {{{#!div |
| | 26 | {{{#!sql |
| | 27 | create materialized view maks_vozenja_po_saat_godisno as |
| | 28 | select |
| | 29 | godina, |
| | 30 | li_ime, |
| | 31 | interval_1h, |
| | 32 | broj_vozenja |
| | 33 | from ( |
| | 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 |
| | 44 | where (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 | ) |
| | 62 | order by godina, li_ime; |
| | 63 | |
| | 64 | create 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 |
| | 72 | create materialized view vozenja_po_linija_godisno as |
| | 73 | select |
| | 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 |
| | 78 | from linija li |
| | 79 | join instanca_na_linija inl on inl.li_id = li.li_id |
| | 80 | join vozenje v on v.inl_id = inl.inl_id |
| | 81 | group by godina, li.li_id, li.li_ime, li.li_pravec |
| | 82 | order by godina, li.li_id desc; |
| | 83 | |
| | 84 | create index idx_vozenja_po_linija on vozenja_po_linija_godisno (li_ime, godina); |
| | 85 | }}} |
| | 86 | }}} |
| | 87 | |
| | 88 | === 4. Број патници според постојка на дадена линија за секоја година === |
| | 89 | |
| | 90 | {{{#!div |
| | 91 | {{{#!sql |
| | 92 | create materialized view patnici_po_postojka_godisno as |
| | 93 | select |
| | 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 |
| | 98 | from postojka p |
| | 99 | join postojka_na_linija pnl on pnl.p_id = p.p_id |
| | 100 | join vozenje v on v.pnl_id = pnl.pnl_id |
| | 101 | join patnik pa on pa.k_id = v.patnik_k_id |
| | 102 | join linija li on li.li_id = pnl.li_id |
| | 103 | group by godina, p.p_ime, li.li_ime |
| | 104 | order by godina, broj_patnici desc; |
| | 105 | |
| | 106 | create 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 |
| | 114 | create materialized view maks_kupeni_bileti_po_cas_godisno as |
| | 115 | select |
| | 116 | tb_ime, |
| | 117 | godina, |
| | 118 | interval_1h, |
| | 119 | broj_bileti |
| | 120 | from ( |
| | 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 |
| | 131 | where (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 | ) |
| | 149 | order by q1.godina, q1.tb_ime; |
| | 150 | |
| | 151 | create 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 |
| | 159 | create materialized view profit_kazni_i_bileti_godisno as |
| | 160 | select |
| | 161 | godina, |
| | 162 | sum(coalesce(kz_iznos, 0)) + sum(coalesce(tb_cena, 0)) as vkupen_promet |
| | 163 | from ( |
| | 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 |
| | 182 | group by godina |
| | 183 | order by godina; |
| | 184 | |
| | 185 | create index idx_prihod_godisen on profit_kazni_i_bileti_godisno(godina); |
| | 186 | }}} |
| | 187 | }}} |
| | 188 | |
| | 189 | = Тригери и фукниции = |
| | 190 | |
| | 191 | === 1. Промена на статус на билет === |
| | 192 | |
| | 193 | {{{#!div |
| | 194 | {{{#!sql |
| | 195 | create or replace function bilet_status() |
| | 196 | returns trigger as |
| | 197 | $$ |
| | 198 | begin |
| | 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; |
| | 221 | end; |
| | 222 | $$ language plpgsql; |
| | 223 | |
| | 224 | create or replace trigger proverka_bilet |
| | 225 | before insert or update on bilet |
| | 226 | for each row |
| | 227 | execute function bilet_status(); |
| | 228 | }}} |
| | 229 | }}} |
| | 230 | |
| | 231 | === 2. Промена на статус на билет === |
| | 232 | |
| | 233 | {{{#!div |
| | 234 | {{{#!sql |
| | 235 | create or replace function vozenje_status() |
| | 236 | returns trigger as $$ |
| | 237 | begin |
| | 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; |
| | 255 | end; |
| | 256 | $$ language plpgsql; |
| | 257 | |
| | 258 | create or replace trigger vozenje_proverka |
| | 259 | before insert or update on vozenje |
| | 260 | for each row |
| | 261 | execute function vozenje_status(); |
| | 262 | }}} |
| | 263 | }}} |
| | 264 | |
| | 265 | = Процеудури и трансакции = |
| | 266 | |
| | 267 | ==== 1. Регистрација на корисник ==== |
| | 268 | |
| | 269 | {{{#!div |
| | 270 | {{{#!sql |
| | 271 | create 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 | ) |
| | 284 | language plpgsql |
| | 285 | as $$ |
| | 286 | declare |
| | 287 | nov_k_id bigint; |
| | 288 | begin |
| | 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; |
| | 336 | exception |
| | 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; |
| | 347 | end; |
| | 348 | $$; |
| | 349 | }}} |
| | 350 | }}} |
| | 351 | |
| | 352 | ==== 2. Запишување казна ==== |
| | 353 | |
| | 354 | {{{#!div |
| | 355 | {{{#!sql |
| | 356 | create 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 | ) |
| | 370 | language plpgsql |
| | 371 | as $$ |
| | 372 | declare |
| | 373 | nova_kz_id bigint; |
| | 374 | begin |
| | 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; |
| | 440 | exception |
| | 441 | when others then |
| | 442 | raise exception 'грешка при запишување казна: %', sqlerrm; |
| | 443 | end; |
| | 444 | $$; |
| | 445 | }}} |
| | 446 | }}} |