| 11 | | Потребно е да се добие аналитички извештај кој ќе покаже кои книги се најуспешни според бројот на продадени примероци и остварениот приход. |
| 12 | | |
| 13 | | За секоја книга се прикажуваат: |
| 14 | | |
| 15 | | - вкупниот број на продадени примероци; |
| 16 | | - вкупниот остварен приход; |
| 17 | | - бројот на различни реализирани нарачки во кои се појавува книгата; |
| 18 | | - просечниот приход од една ставка во нарачка. |
| 19 | | |
| 20 | | Во извештајот се земаат предвид само нарачките со статус ''ZAVRSENA'', бидејќи тие претставуваат реализирани купувања. |
| 21 | | |
| 22 | | Извештајот може да се користи за анализа на продажбата и за идентификување на книгите кои генерираат најголем приход. |
| 23 | | |
| 24 | | === Решение во SQL === |
| 25 | | |
| 26 | | {{{ |
| | 27 | Целта на извештајот е да се анализира како се менува продажбата на книгите и категориите низ кварталите и да се идентификуваат книгите кои имаат најголемо влијание врз приходот. |
| | 28 | |
| | 29 | За секој квартал, категорија и книга потребно е да се прикажат: |
| | 30 | |
| | 31 | * бројот на различни реализирани нарачки; |
| | 32 | * вкупниот број на продадени примероци; |
| | 33 | * вкупниот остварен приход; |
| | 34 | * просечната продажна цена; |
| | 35 | * вкупниот приход на категоријата во кварталот; |
| | 36 | * просечниот приход по продавана книга во категоријата; |
| | 37 | * рангот на книгата во рамки на категоријата за конкретниот квартал; |
| | 38 | * процентуалното учество на книгата во приходот на категоријата; |
| | 39 | * приходот на истата книга во претходниот достапен квартал; |
| | 40 | * процентуалната промена на приходот во однос на претходниот достапен квартал; |
| | 41 | * кумулативниот приход на книгата низ кварталите; |
| | 42 | * класификација на трендот на продажба. |
| | 43 | |
| | 44 | Извештајот овозможува квартално, полугодишно, годишно и повеќегодишно следење на продажбата. |
| | 45 | |
| | 46 | Со него може да се утврди кои книги носат најголем приход во својата категорија, како нивната продажба се менува низ времето и дали одредена книга покажува растечки или опаѓачки тренд. |
| | 47 | |
| | 48 | |
| | 49 | === Solution SQL === |
| | 50 | |
| | 51 | {{{ |
| | 52 | WITH quarterly_book_sales AS ( |
| | 53 | SELECT |
| | 54 | DATE_TRUNC('quarter', n.datum) AS kvartal, |
| | 55 | k.kniga_id, |
| | 56 | k.naslov, |
| | 57 | k.isbn, |
| | 58 | kat.kategorija_id, |
| | 59 | kat.naziv AS kategorija, |
| | 60 | |
| | 61 | COUNT(DISTINCT n.naracka_id) AS broj_naracki, |
| | 62 | |
| | 63 | SUM(s.kolicina) AS vkupno_prodadeni, |
| | 64 | |
| | 65 | SUM( |
| | 66 | s.kolicina * s.edinechna_cena |
| | 67 | ) AS vkupen_prihod, |
| | 68 | |
| | 69 | AVG( |
| | 70 | s.edinechna_cena |
| | 71 | ) AS prosecna_prodazna_cena |
| | 72 | |
| | 73 | FROM project.knigi k |
| | 74 | |
| | 75 | JOIN project.ima_kategorija ik |
| | 76 | ON k.kniga_id = ik.kniga_id |
| | 77 | |
| | 78 | JOIN project.kategorii kat |
| | 79 | ON ik.kategorija_id = kat.kategorija_id |
| | 80 | |
| | 81 | JOIN project.sodrzi s |
| | 82 | ON k.kniga_id = s.kniga_id |
| | 83 | |
| | 84 | JOIN project.naracki n |
| | 85 | ON s.naracka_id = n.naracka_id |
| | 86 | |
| | 87 | WHERE n.status = 'ZAVRSENA' |
| | 88 | |
| | 89 | GROUP BY |
| | 90 | DATE_TRUNC('quarter', n.datum), |
| | 91 | k.kniga_id, |
| | 92 | k.naslov, |
| | 93 | k.isbn, |
| | 94 | kat.kategorija_id, |
| | 95 | kat.naziv |
| | 96 | ), |
| | 97 | |
| | 98 | category_statistics AS ( |
| | 99 | SELECT |
| | 100 | kvartal, |
| | 101 | kategorija_id, |
| | 102 | kategorija, |
| | 103 | |
| | 104 | COUNT(DISTINCT kniga_id) |
| | 105 | AS broj_prodavani_knigi, |
| | 106 | |
| | 107 | SUM(vkupno_prodadeni) |
| | 108 | AS vkupno_prodadeni_kategorija, |
| | 109 | |
| | 110 | SUM(vkupen_prihod) |
| | 111 | AS vkupen_prihod_kategorija, |
| | 112 | |
| | 113 | AVG(vkupen_prihod) |
| | 114 | AS prosechen_prihod_po_kniga |
| | 115 | |
| | 116 | FROM quarterly_book_sales |
| | 117 | |
| | 118 | GROUP BY |
| | 119 | kvartal, |
| | 120 | kategorija_id, |
| | 121 | kategorija |
| | 122 | ), |
| | 123 | |
| | 124 | quarterly_analysis AS ( |
| | 125 | SELECT |
| | 126 | qbs.kvartal, |
| | 127 | qbs.kniga_id, |
| | 128 | qbs.naslov, |
| | 129 | qbs.isbn, |
| | 130 | qbs.kategorija_id, |
| | 131 | qbs.kategorija, |
| | 132 | qbs.broj_naracki, |
| | 133 | qbs.vkupno_prodadeni, |
| | 134 | qbs.vkupen_prihod, |
| | 135 | qbs.prosecna_prodazna_cena, |
| | 136 | |
| | 137 | cs.broj_prodavani_knigi, |
| | 138 | cs.vkupno_prodadeni_kategorija, |
| | 139 | cs.vkupen_prihod_kategorija, |
| | 140 | cs.prosechen_prihod_po_kniga, |
| | 141 | |
| | 142 | RANK() OVER ( |
| | 143 | PARTITION BY |
| | 144 | qbs.kvartal, |
| | 145 | qbs.kategorija_id |
| | 146 | ORDER BY |
| | 147 | qbs.vkupen_prihod DESC |
| | 148 | ) AS rang_vo_kategorija, |
| | 149 | |
| | 150 | LAG(qbs.vkupen_prihod) OVER ( |
| | 151 | PARTITION BY |
| | 152 | qbs.kniga_id, |
| | 153 | qbs.kategorija_id |
| | 154 | ORDER BY |
| | 155 | qbs.kvartal |
| | 156 | ) AS prihod_prethoden_kvartal, |
| | 157 | |
| | 158 | SUM(qbs.vkupen_prihod) OVER ( |
| | 159 | PARTITION BY |
| | 160 | qbs.kniga_id, |
| | 161 | qbs.kategorija_id |
| | 162 | ORDER BY |
| | 163 | qbs.kvartal |
| | 164 | ROWS BETWEEN |
| | 165 | UNBOUNDED PRECEDING |
| | 166 | AND CURRENT ROW |
| | 167 | ) AS kumulativen_prihod |
| | 168 | |
| | 169 | FROM quarterly_book_sales qbs |
| | 170 | |
| | 171 | JOIN category_statistics cs |
| | 172 | ON qbs.kvartal = cs.kvartal |
| | 173 | AND qbs.kategorija_id = cs.kategorija_id |
| | 174 | ), |
| | 175 | |
| | 176 | final_analysis AS ( |
| | 177 | SELECT |
| | 178 | *, |
| | 179 | |
| | 180 | ROUND( |
| | 181 | ( |
| | 182 | vkupen_prihod |
| | 183 | / |
| | 184 | NULLIF( |
| | 185 | vkupen_prihod_kategorija, |
| | 186 | 0 |
| | 187 | ) |
| | 188 | ) * 100, |
| | 189 | 2 |
| | 190 | ) AS procent_od_prihod_na_kategorija, |
| | 191 | |
| | 192 | ROUND( |
| | 193 | ( |
| | 194 | ( |
| | 195 | vkupen_prihod - |
| | 196 | prihod_prethoden_kvartal |
| | 197 | ) |
| | 198 | / |
| | 199 | NULLIF( |
| | 200 | prihod_prethoden_kvartal, |
| | 201 | 0 |
| | 202 | ) |
| | 203 | ) * 100, |
| | 204 | 2 |
| | 205 | ) AS promena_prihod_procent, |
| | 206 | |
| | 207 | CASE |
| | 208 | WHEN prihod_prethoden_kvartal IS NULL |
| | 209 | THEN 'NEMA PRETHODEN PERIOD' |
| | 210 | |
| | 211 | WHEN vkupen_prihod > |
| | 212 | prihod_prethoden_kvartal * 1.10 |
| | 213 | THEN 'RAST' |
| | 214 | |
| | 215 | WHEN vkupen_prihod < |
| | 216 | prihod_prethoden_kvartal * 0.90 |
| | 217 | THEN 'PAD' |
| | 218 | |
| | 219 | ELSE 'STABILNO' |
| | 220 | END AS trend |
| | 221 | |
| | 222 | FROM quarterly_analysis |
| | 223 | ) |
| | 224 | |
| 37 | | ) AS prosechen_prihod_od_stavka |
| 38 | | FROM project.knigi k |
| 39 | | JOIN project.sodrzi s |
| 40 | | ON k.kniga_id = s.kniga_id |
| 41 | | JOIN project.naracki n |
| 42 | | ON s.naracka_id = n.naracka_id |
| 43 | | WHERE n.status = 'ZAVRSENA' |
| 44 | | GROUP BY |
| 45 | | k.kniga_id, |
| 46 | | k.naslov, |
| 47 | | k.isbn |
| 48 | | HAVING SUM(s.kolicina) > 0 |
| 49 | | ORDER BY |
| 50 | | vkupen_prihod DESC; |
| 51 | | }}} |
| 52 | | |
| 53 | | === Решение во релациона алгебра === |
| 54 | | |
| 55 | | {{{ |
| 56 | | R1 ← KNIGI ⋈ KNIGI.kniga_id = SODRZI.kniga_id SODRZI |
| 57 | | |
| 58 | | R2 ← R1 ⋈ SODRZI.naracka_id = NARACKI.naracka_id |
| 59 | | σ(status = 'ZAVRSENA')(NARACKI) |
| 60 | | |
| 61 | | R3 ← γ |
| 62 | | kniga_id, naslov, isbn; |
| 63 | | SUM(kolicina) → vkupno_prodadeni, |
| 64 | | SUM(kolicina × edinechna_cena) → vkupen_prihod, |
| 65 | | COUNT-DISTINCT(naracka_id) → broj_naracki, |
| 66 | | AVG(kolicina × edinechna_cena) → prosechen_prihod_od_stavka |
| 67 | | (R2) |
| 68 | | |
| 69 | | R4 ← σ(vkupno_prodadeni > 0)(R3) |
| 70 | | |
| 71 | | Result ← τ vkupen_prihod DESC |
| 72 | | (R4) |
| 73 | | }}} |
| 74 | | |
| 75 | | === Објаснување === |
| 76 | | |
| 77 | | Прво се спојуваат книгите со ставките од нарачките преку ''kniga_id''. Потоа резултатот се спојува со нарачките преку ''naracka_id'', при што се задржуваат само нарачките со статус ''ZAVRSENA''. |
| 78 | | |
| 79 | | Со γ се врши групирање по книга и се пресметуваат вкупниот број на продадени примероци, вкупниот приход, бројот на различни нарачки и просечниот приход од ставка. |
| 80 | | |
| 81 | | Со σ се задржуваат само книгите за кои има продадени примероци. |
| 82 | | |
| 83 | | На крај, со τ резултатите се подредуваат според вкупниот приход во опаѓачки редослед. |
| 84 | | |
| 85 | | == Проценка на ризик од исцрпување на залихата == |
| 86 | | |
| 87 | | === Data requirements description === |
| 88 | | |
| 89 | | Потребно е да се добие аналитички извештај кој ќе идентификува книги кај кои постои потенцијален ризик од исцрпување на моменталната залиха. |
| 90 | | |
| 91 | | За секоја книга се прикажуваат: |
| 92 | | |
| 93 | | - моменталната залиха; |
| 94 | | - вкупниот број на продадени примероци; |
| 95 | | - бројот на реализирани нарачки; |
| 96 | | - просечната продажба по нарачка; |
| 97 | | - проценетиот број на нарачки до исцрпување на моменталната залиха. |
| 98 | | |
| 99 | | Извештајот ги зема предвид само реализираните нарачки со статус ''ZAVRSENA''. |
| 100 | | |
| 101 | | Просечната продажба по нарачка се пресметува врз основа на вкупниот број на продадени примероци и бројот на различни реализирани нарачки. |
| 102 | | |
| 103 | | Проценетиот број на нарачки до исцрпување на залихата се добива со споредување на моменталната залиха со просечната продажба по нарачка. |
| 104 | | |
| 105 | | Помал број на проценети нарачки до исцрпување укажува дека моменталната залиха би се потрошила со помал број на просечни нарачки. |
| 106 | | |
| 107 | | Овој извештај може да се користи за следење на залихите и навремено планирање на дополнување на книгите. |
| 108 | | |
| 109 | | === Решение во SQL === |
| 110 | | |
| 111 | | {{{ |
| 112 | | SELECT |
| 113 | | k.kniga_id, |
| 114 | | k.naslov, |
| 115 | | k.kolicina_na_zaliha AS momentalna_zaliha, |
| 116 | | |
| 117 | | SUM(s.kolicina) AS vkupno_prodadeni, |
| 118 | | |
| 119 | | COUNT(DISTINCT n.naracka_id) AS broj_naracki, |
| | 242 | ) AS vkupen_prihod, |
| 154 | | proceneti_naracki_do_istekuvanje ASC; |
| 155 | | }}} |
| 156 | | |
| 157 | | === Решение во релациона алгебра === |
| 158 | | |
| 159 | | {{{ |
| 160 | | R1 ← KNIGI ⋈ KNIGI.kniga_id = SODRZI.kniga_id SODRZI |
| 161 | | |
| 162 | | R2 ← R1 ⋈ SODRZI.naracka_id = NARACKI.naracka_id |
| 163 | | σ(status = 'ZAVRSENA')(NARACKI) |
| 164 | | |
| 165 | | R3 ← γ |
| 166 | | kniga_id, naslov, kolicina_na_zaliha; |
| 167 | | SUM(kolicina) → vkupno_prodadeni, |
| 168 | | COUNT-DISTINCT(naracka_id) → broj_naracki |
| 169 | | (R2) |
| 170 | | |
| 171 | | R4 ← σ(vkupno_prodadeni > 0)(R3) |
| 172 | | |
| 173 | | R5 ← π |
| 174 | | kniga_id, |
| 175 | | naslov, |
| 176 | | kolicina_na_zaliha, |
| 177 | | vkupno_prodadeni, |
| 178 | | broj_naracki, |
| 179 | | ( |
| 180 | | vkupno_prodadeni / broj_naracki |
| 181 | | ) → prosecna_prodazba_po_naracka, |
| 182 | | ( |
| 183 | | kolicina_na_zaliha / |
| 184 | | (vkupno_prodadeni / broj_naracki) |
| 185 | | ) → proceneti_naracki_do_istekuvanje |
| 186 | | (R4) |
| 187 | | |
| 188 | | Result ← τ proceneti_naracki_do_istekuvanje ASC |
| 189 | | (R5) |
| 190 | | }}} |
| | 287 | kvartal, |
| | 288 | kategorija, |
| | 289 | rang_vo_kategorija, |
| | 290 | naslov; |
| | 291 | }}} |
| | 292 | |
| | 293 | |
| | 294 | === Solution Relational Algebra === |
| | 295 | |
| | 296 | Најпрво се издвојуваат само реализираните нарачки: |
| | 297 | |
| | 298 | {{{ |
| | 299 | N ← σ status='ZAVRSENA' (NARACKI) |
| | 300 | }}} |
| | 301 | |
| | 302 | Книгите се поврзуваат со категориите: |
| | 303 | |
| | 304 | {{{ |
| | 305 | KC ← |
| | 306 | KNIGI |
| | 307 | ⋈ KNIGI.kniga_id = IMA_KATEGORIJA.kniga_id |
| | 308 | IMA_KATEGORIJA |
| | 309 | ⋈ IMA_KATEGORIJA.kategorija_id = KATEGORII.kategorija_id |
| | 310 | KATEGORII |
| | 311 | }}} |
| | 312 | |
| | 313 | Потоа се додаваат ставките и реализираните нарачки: |
| | 314 | |
| | 315 | {{{ |
| | 316 | R1 ← |
| | 317 | KC |
| | 318 | ⋈ KNIGI.kniga_id = SODRZI.kniga_id |
| | 319 | SODRZI |
| | 320 | |
| | 321 | R2 ← |
| | 322 | R1 |
| | 323 | ⋈ SODRZI.naracka_id = N.naracka_id |
| | 324 | N |
| | 325 | }}} |
| | 326 | |
| | 327 | Датумот се трансформира во квартал и се врши првото ниво на агрегација: |
| | 328 | |
| | 329 | {{{ |
| | 330 | QBS ← |
| | 331 | quarter(datum), |
| | 332 | kniga_id, |
| | 333 | naslov, |
| | 334 | isbn, |
| | 335 | kategorija_id, |
| | 336 | kategorija |
| | 337 | |
| | 338 | γ |
| | 339 | |
| | 340 | COUNT-DISTINCT(naracka_id) |
| | 341 | → broj_naracki, |
| | 342 | |
| | 343 | SUM(kolicina) |
| | 344 | → vkupno_prodadeni, |
| | 345 | |
| | 346 | SUM(kolicina × edinechna_cena) |
| | 347 | → vkupen_prihod, |
| | 348 | |
| | 349 | AVG(edinechna_cena) |
| | 350 | → prosecna_prodazna_cena |
| | 351 | |
| | 352 | (R2) |
| | 353 | }}} |
| | 354 | |
| | 355 | Второто ниво на агрегација се врши на ниво квартал–категорија: |
| | 356 | |
| | 357 | {{{ |
| | 358 | CS ← |
| | 359 | kvartal, |
| | 360 | kategorija_id, |
| | 361 | kategorija |
| | 362 | |
| | 363 | γ |
| | 364 | |
| | 365 | COUNT-DISTINCT(kniga_id) |
| | 366 | → broj_prodavani_knigi, |
| | 367 | |
| | 368 | SUM(vkupno_prodadeni) |
| | 369 | → vkupno_prodadeni_kategorija, |
| | 370 | |
| | 371 | SUM(vkupen_prihod) |
| | 372 | → vkupen_prihod_kategorija, |
| | 373 | |
| | 374 | AVG(vkupen_prihod) |
| | 375 | → prosechen_prihod_po_kniga |
| | 376 | |
| | 377 | (QBS) |
| | 378 | }}} |
| | 379 | |
| | 380 | Двете нивоа на анализа се поврзуваат: |
| | 381 | |
| | 382 | {{{ |
| | 383 | QA ← |
| | 384 | QBS |
| | 385 | ⋈ QBS.kvartal = CS.kvartal |
| | 386 | ∧ QBS.kategorija_id = CS.kategorija_id |
| | 387 | CS |
| | 388 | }}} |
| | 389 | |
| | 390 | Рангирањето во рамки на квартал и категорија се изразува со проширена аналитичка операција: |
| | 391 | |
| | 392 | {{{ |
| | 393 | QA1 ← |
| | 394 | RANK() |
| | 395 | OVER ( |
| | 396 | PARTITION BY kvartal, kategorija_id |
| | 397 | ORDER BY vkupen_prihod DESC |
| | 398 | ) |
| | 399 | (QA) |
| | 400 | }}} |
| | 401 | |
| | 402 | Приходот од претходниот достапен квартален запис се добива со: |
| | 403 | |
| | 404 | {{{ |
| | 405 | QA2 ← |
| | 406 | LAG(vkupen_prihod) |
| | 407 | OVER ( |
| | 408 | PARTITION BY kniga_id, kategorija_id |
| | 409 | ORDER BY kvartal |
| | 410 | ) |
| | 411 | (QA1) |
| | 412 | }}} |
| | 413 | |
| | 414 | Кумулативниот приход се добива со: |
| | 415 | |
| | 416 | {{{ |
| | 417 | QA3 ← |
| | 418 | CUMULATIVE_SUM(vkupen_prihod) |
| | 419 | OVER ( |
| | 420 | PARTITION BY kniga_id, kategorija_id |
| | 421 | ORDER BY kvartal |
| | 422 | ) |
| | 423 | (QA2) |
| | 424 | }}} |
| | 425 | |
| | 426 | Се пресметуваат процентуалното учество и промената на приходот: |
| | 427 | |
| | 428 | {{{ |
| | 429 | FA ← π |
| | 430 | kvartal, |
| | 431 | kategorija, |
| | 432 | kniga_id, |
| | 433 | naslov, |
| | 434 | isbn, |
| | 435 | broj_naracki, |
| | 436 | vkupno_prodadeni, |
| | 437 | vkupen_prihod, |
| | 438 | prosecna_prodazna_cena, |
| | 439 | broj_prodavani_knigi, |
| | 440 | vkupno_prodadeni_kategorija, |
| | 441 | vkupen_prihod_kategorija, |
| | 442 | prosechen_prihod_po_kniga, |
| | 443 | rang_vo_kategorija, |
| | 444 | |
| | 445 | ( |
| | 446 | 100 × |
| | 447 | vkupen_prihod / |
| | 448 | vkupen_prihod_kategorija |
| | 449 | ) |
| | 450 | → procent_od_prihod_na_kategorija, |
| | 451 | |
| | 452 | prihod_prethoden_kvartal, |
| | 453 | |
| | 454 | ( |
| | 455 | 100 × |
| | 456 | ( |
| | 457 | vkupen_prihod - |
| | 458 | prihod_prethoden_kvartal |
| | 459 | ) |
| | 460 | / |
| | 461 | prihod_prethoden_kvartal |
| | 462 | ) |
| | 463 | → promena_prihod_procent, |
| | 464 | |
| | 465 | kumulativen_prihod |
| | 466 | |
| | 467 | (QA3) |
| | 468 | }}} |
| | 469 | |
| | 470 | Конечниот резултат се подредува според квартал, категорија и ранг: |
| | 471 | |
| | 472 | {{{ |
| | 473 | RESULT ← |
| | 474 | τ kvartal ASC, |
| | 475 | kategorija ASC, |
| | 476 | rang_vo_kategorija ASC |
| | 477 | (FA) |
| | 478 | }}} |
| | 479 | |
| 194 | | Прво се спојуваат книгите со ставките од нарачките, а потоа со нарачките. Се задржуваат само реализираните нарачки со статус ''ZAVRSENA''. |
| 195 | | |
| 196 | | Со γ се групираат податоците според книга и се пресметуваат вкупниот број на продадени примероци и бројот на различни реализирани нарачки. |
| 197 | | |
| 198 | | Просечната продажба по нарачка се добива со делење на вкупниот број на продадени примероци со бројот на реализирани нарачки. |
| 199 | | |
| 200 | | Проценетиот број на нарачки до исцрпување на залихата се добива со делење на моменталната залиха со просечната продажба по нарачка. |
| 201 | | |
| 202 | | Со τ резултатите се подредуваат растечки според проценетиот број на нарачки до исцрпување на залихата. |
| | 483 | Извештајот користи повеќе нивоа на обработка. |
| | 484 | |
| | 485 | Во ''quarterly_book_sales'' податоците од пет релации се поврзуваат и се агрегираат на ниво квартал–книга–категорија. |
| | 486 | |
| | 487 | Со: |
| | 488 | |
| | 489 | {{{ |
| | 490 | DATE_TRUNC('quarter', n.datum) |
| | 491 | }}} |
| | 492 | |
| | 493 | датумите на нарачките се претвораат во квартални периоди. |
| | 494 | |
| | 495 | За секоја книга се пресметуваат бројот на реализирани нарачки, бројот на продадени примероци, приходот и просечната продажна цена. |
| | 496 | |
| | 497 | Во ''category_statistics'' се врши второ ниво на агрегација. Податоците кои претходно биле пресметани по книга повторно се агрегираат на ниво квартал–категорија. |
| | 498 | |
| | 499 | На овој начин може да се спореди индивидуалниот резултат на книгата со резултатите на целата категорија. |
| | 500 | |
| | 501 | Со: |
| | 502 | |
| | 503 | {{{ |
| | 504 | RANK() OVER ( |
| | 505 | PARTITION BY qbs.kvartal, qbs.kategorija_id |
| | 506 | ORDER BY qbs.vkupen_prihod DESC |
| | 507 | ) |
| | 508 | }}} |
| | 509 | |
| | 510 | секоја книга добива ранг во својата категорија за конкретниот квартал. |
| | 511 | |
| | 512 | Со ''LAG()'' се добива приходот од претходниот достапен квартален запис за истата книга и категорија. |
| | 513 | |
| | 514 | Ако книгата нема продажба во некој календарски квартал, тој квартал не создава посебен ред со вредност 0. Поради тоа ''LAG()'' се однесува на претходниот достапен квартален запис. |
| | 515 | |
| | 516 | Со прозорска ''SUM()'' се пресметува кумулативниот приход на книгата низ кварталите. |
| | 517 | |
| | 518 | Потоа се пресметуваат процентуалното учество во приходот на категоријата и процентуалната промена во однос на претходниот достапен период. |
| | 519 | |
| | 520 | Со ''CASE'' трендот се класифицира како ''RAST'', ''PAD'', ''STABILNO'' или ''NEMA PRETHODEN PERIOD''. |
| | 521 | |
| | 522 | |
| | 523 | == Временска анализа на продажбата и прогноза на ризик од исцрпување на залихата == |
| | 524 | |
| | 525 | === Data requirements description === |
| | 526 | |
| | 527 | Целта на вториот извештај е да се анализира долгорочната продажна динамика на книгите и врз основа на историската продажба да се направи проценка кои книги имаат најголем ризик од исцрпување на залихата. |
| | 528 | |
| | 529 | За да не се користи само вкупната историска продажба, реализираните продажби најпрво се агрегираат на месечно ниво. |
| | 530 | |
| | 531 | За секоја книга се анализираат: |
| | 532 | |
| | 533 | * продажбата во последниот достапен месечен период; |
| | 534 | * продажбата во претходниот достапен месечен период; |
| | 535 | * приходот во последниот период; |
| | 536 | * приходот во претходниот период; |
| | 537 | * бројот на различни нарачки; |
| | 538 | * подвижниот просек на продажбата над тековниот и најмногу двата претходни достапни месечни записи; |
| | 539 | * кумулативниот приход; |
| | 540 | * процентуалната промена на продажбата; |
| | 541 | * моменталната залиха; |
| | 542 | * проценетиот број на месеци до исцрпување на залихата; |
| | 543 | * ниво на ризик; |
| | 544 | * глобален ранг според ризикот. |
| | 545 | |
| | 546 | Подвижниот просек се користи за прогнозата да не зависи само од продажбата во еден период. |
| | 547 | |
| | 548 | Извештајот може да се користи за планирање на дополнување на залихите и за идентификување на книги чија моментална залиха може брзо да се потроши. |
| | 549 | |
| | 550 | |
| | 551 | === Solution SQL === |
| | 552 | |
| | 553 | {{{ |
| | 554 | WITH monthly_book_sales AS ( |
| | 555 | SELECT |
| | 556 | k.kniga_id, |
| | 557 | k.naslov, |
| | 558 | |
| | 559 | DATE_TRUNC( |
| | 560 | 'month', |
| | 561 | n.datum |
| | 562 | ) AS mesec, |
| | 563 | |
| | 564 | SUM( |
| | 565 | s.kolicina |
| | 566 | ) AS prodadeni, |
| | 567 | |
| | 568 | SUM( |
| | 569 | s.kolicina * |
| | 570 | s.edinechna_cena |
| | 571 | ) AS prihod, |
| | 572 | |
| | 573 | COUNT( |
| | 574 | DISTINCT n.naracka_id |
| | 575 | ) AS broj_naracki |
| | 576 | |
| | 577 | FROM project.knigi k |
| | 578 | |
| | 579 | JOIN project.sodrzi s |
| | 580 | ON k.kniga_id = s.kniga_id |
| | 581 | |
| | 582 | JOIN project.naracki n |
| | 583 | ON s.naracka_id = n.naracka_id |
| | 584 | |
| | 585 | WHERE n.status = 'ZAVRSENA' |
| | 586 | |
| | 587 | GROUP BY |
| | 588 | k.kniga_id, |
| | 589 | k.naslov, |
| | 590 | DATE_TRUNC( |
| | 591 | 'month', |
| | 592 | n.datum |
| | 593 | ) |
| | 594 | ), |
| | 595 | |
| | 596 | sales_with_history AS ( |
| | 597 | SELECT |
| | 598 | kniga_id, |
| | 599 | naslov, |
| | 600 | mesec, |
| | 601 | prodadeni, |
| | 602 | prihod, |
| | 603 | broj_naracki, |
| | 604 | |
| | 605 | LAG(prodadeni) OVER ( |
| | 606 | PARTITION BY kniga_id |
| | 607 | ORDER BY mesec |
| | 608 | ) AS prodadeni_prethoden_mesec, |
| | 609 | |
| | 610 | LAG(prihod) OVER ( |
| | 611 | PARTITION BY kniga_id |
| | 612 | ORDER BY mesec |
| | 613 | ) AS prihod_prethoden_mesec, |
| | 614 | |
| | 615 | AVG(prodadeni) OVER ( |
| | 616 | PARTITION BY kniga_id |
| | 617 | ORDER BY mesec |
| | 618 | ROWS BETWEEN |
| | 619 | 2 PRECEDING |
| | 620 | AND CURRENT ROW |
| | 621 | ) AS podvizen_prosek_prodazba, |
| | 622 | |
| | 623 | SUM(prihod) OVER ( |
| | 624 | PARTITION BY kniga_id |
| | 625 | ORDER BY mesec |
| | 626 | ROWS BETWEEN |
| | 627 | UNBOUNDED PRECEDING |
| | 628 | AND CURRENT ROW |
| | 629 | ) AS kumulativen_prihod |
| | 630 | |
| | 631 | FROM monthly_book_sales |
| | 632 | ), |
| | 633 | |
| | 634 | latest_period AS ( |
| | 635 | SELECT |
| | 636 | *, |
| | 637 | |
| | 638 | ROW_NUMBER() OVER ( |
| | 639 | PARTITION BY kniga_id |
| | 640 | ORDER BY mesec DESC |
| | 641 | ) AS rn |
| | 642 | |
| | 643 | FROM sales_with_history |
| | 644 | ), |
| | 645 | |
| | 646 | risk_analysis AS ( |
| | 647 | SELECT |
| | 648 | lp.kniga_id, |
| | 649 | lp.naslov, |
| | 650 | lp.mesec, |
| | 651 | |
| | 652 | k.kolicina_na_zaliha, |
| | 653 | |
| | 654 | lp.prodadeni |
| | 655 | AS prodadeni_posleden_mesec, |
| | 656 | |
| | 657 | lp.prodadeni_prethoden_mesec, |
| | 658 | |
| | 659 | lp.prihod |
| | 660 | AS prihod_posleden_mesec, |
| | 661 | |
| | 662 | lp.prihod_prethoden_mesec, |
| | 663 | |
| | 664 | lp.broj_naracki, |
| | 665 | |
| | 666 | lp.podvizen_prosek_prodazba, |
| | 667 | |
| | 668 | lp.kumulativen_prihod, |
| | 669 | |
| | 670 | ROUND( |
| | 671 | ( |
| | 672 | ( |
| | 673 | lp.prodadeni - |
| | 674 | lp.prodadeni_prethoden_mesec |
| | 675 | )::numeric |
| | 676 | / |
| | 677 | NULLIF( |
| | 678 | lp.prodadeni_prethoden_mesec, |
| | 679 | 0 |
| | 680 | ) |
| | 681 | ) * 100, |
| | 682 | 2 |
| | 683 | ) AS rast_prodazba_procent, |
| | 684 | |
| | 685 | ROUND( |
| | 686 | k.kolicina_na_zaliha::numeric |
| | 687 | / |
| | 688 | NULLIF( |
| | 689 | lp.podvizen_prosek_prodazba, |
| | 690 | 0 |
| | 691 | ), |
| | 692 | 2 |
| | 693 | ) AS proceneti_meseci_do_istekuvanje |
| | 694 | |
| | 695 | FROM latest_period lp |
| | 696 | |
| | 697 | JOIN project.knigi k |
| | 698 | ON lp.kniga_id = k.kniga_id |
| | 699 | |
| | 700 | WHERE lp.rn = 1 |
| | 701 | ), |
| | 702 | |
| | 703 | final_analysis AS ( |
| | 704 | SELECT |
| | 705 | *, |
| | 706 | |
| | 707 | CASE |
| | 708 | WHEN proceneti_meseci_do_istekuvanje <= 1 |
| | 709 | AND COALESCE( |
| | 710 | rast_prodazba_procent, |
| | 711 | 0 |
| | 712 | ) > 0 |
| | 713 | THEN 'KRITICEN RIZIK' |
| | 714 | |
| | 715 | WHEN proceneti_meseci_do_istekuvanje <= 2 |
| | 716 | THEN 'VISOK RIZIK' |
| | 717 | |
| | 718 | WHEN proceneti_meseci_do_istekuvanje <= 4 |
| | 719 | THEN 'SREDEN RIZIK' |
| | 720 | |
| | 721 | ELSE 'NIZOK RIZIK' |
| | 722 | END AS nivo_na_rizik, |
| | 723 | |
| | 724 | DENSE_RANK() OVER ( |
| | 725 | ORDER BY |
| | 726 | proceneti_meseci_do_istekuvanje |
| | 727 | ASC NULLS LAST |
| | 728 | ) AS rang_rizik |
| | 729 | |
| | 730 | FROM risk_analysis |
| | 731 | ) |
| | 732 | |
| | 733 | SELECT |
| | 734 | kniga_id, |
| | 735 | naslov, |
| | 736 | |
| | 737 | TO_CHAR( |
| | 738 | mesec, |
| | 739 | 'YYYY-MM' |
| | 740 | ) AS posleden_period, |
| | 741 | |
| | 742 | kolicina_na_zaliha, |
| | 743 | |
| | 744 | prodadeni_posleden_mesec, |
| | 745 | |
| | 746 | prodadeni_prethoden_mesec, |
| | 747 | |
| | 748 | broj_naracki, |
| | 749 | |
| | 750 | ROUND( |
| | 751 | prihod_posleden_mesec, |
| | 752 | 2 |
| | 753 | ) AS prihod_posleden_mesec, |
| | 754 | |
| | 755 | ROUND( |
| | 756 | COALESCE( |
| | 757 | prihod_prethoden_mesec, |
| | 758 | 0 |
| | 759 | ), |
| | 760 | 2 |
| | 761 | ) AS prihod_prethoden_mesec, |
| | 762 | |
| | 763 | ROUND( |
| | 764 | podvizen_prosek_prodazba, |
| | 765 | 2 |
| | 766 | ) AS podvizen_prosek_prodazba, |
| | 767 | |
| | 768 | ROUND( |
| | 769 | kumulativen_prihod, |
| | 770 | 2 |
| | 771 | ) AS kumulativen_prihod, |
| | 772 | |
| | 773 | rast_prodazba_procent, |
| | 774 | |
| | 775 | proceneti_meseci_do_istekuvanje, |
| | 776 | |
| | 777 | nivo_na_rizik, |
| | 778 | |
| | 779 | rang_rizik |
| | 780 | |
| | 781 | FROM final_analysis |
| | 782 | |
| | 783 | ORDER BY |
| | 784 | rang_rizik, |
| | 785 | naslov; |
| | 786 | }}} |
| | 787 | |
| | 788 | |
| | 789 | === Solution Relational Algebra === |
| | 790 | |
| | 791 | Најпрво се издвојуваат реализираните нарачки: |
| | 792 | |
| | 793 | {{{ |
| | 794 | N ← σ status='ZAVRSENA' (NARACKI) |
| | 795 | }}} |
| | 796 | |
| | 797 | Се поврзуваат книгите, ставките и реализираните нарачки: |
| | 798 | |
| | 799 | {{{ |
| | 800 | R1 ← |
| | 801 | KNIGI |
| | 802 | ⋈ KNIGI.kniga_id = SODRZI.kniga_id |
| | 803 | SODRZI |
| | 804 | |
| | 805 | R2 ← |
| | 806 | R1 |
| | 807 | ⋈ SODRZI.naracka_id = N.naracka_id |
| | 808 | N |
| | 809 | }}} |
| | 810 | |
| | 811 | Податоците се агрегираат на ниво книга–месец: |
| | 812 | |
| | 813 | {{{ |
| | 814 | MBS ← |
| | 815 | kniga_id, |
| | 816 | naslov, |
| | 817 | month(datum)→mesec |
| | 818 | |
| | 819 | γ |
| | 820 | |
| | 821 | SUM(kolicina) |
| | 822 | → prodadeni, |
| | 823 | |
| | 824 | SUM(kolicina × edinechna_cena) |
| | 825 | → prihod, |
| | 826 | |
| | 827 | COUNT-DISTINCT(naracka_id) |
| | 828 | → broj_naracki |
| | 829 | |
| | 830 | (R2) |
| | 831 | }}} |
| | 832 | |
| | 833 | Продажбата од претходниот достапен период се добива со: |
| | 834 | |
| | 835 | {{{ |
| | 836 | H1 ← |
| | 837 | LAG(prodadeni) |
| | 838 | OVER ( |
| | 839 | PARTITION BY kniga_id |
| | 840 | ORDER BY mesec |
| | 841 | ) |
| | 842 | (MBS) |
| | 843 | }}} |
| | 844 | |
| | 845 | Приходот од претходниот достапен период се добива со: |
| | 846 | |
| | 847 | {{{ |
| | 848 | H2 ← |
| | 849 | LAG(prihod) |
| | 850 | OVER ( |
| | 851 | PARTITION BY kniga_id |
| | 852 | ORDER BY mesec |
| | 853 | ) |
| | 854 | (H1) |
| | 855 | }}} |
| | 856 | |
| | 857 | Подвижниот просек се пресметува над тековниот и најмногу двата претходни достапни месечни записи: |
| | 858 | |
| | 859 | {{{ |
| | 860 | H3 ← |
| | 861 | MOVING_AVG(prodadeni) |
| | 862 | OVER ( |
| | 863 | PARTITION BY kniga_id |
| | 864 | ORDER BY mesec |
| | 865 | ROWS BETWEEN |
| | 866 | 2 PRECEDING |
| | 867 | AND CURRENT ROW |
| | 868 | ) |
| | 869 | (H2) |
| | 870 | }}} |
| | 871 | |
| | 872 | Кумулативниот приход се пресметува од првиот достапен период до тековниот: |
| | 873 | |
| | 874 | {{{ |
| | 875 | H4 ← |
| | 876 | CUMULATIVE_SUM(prihod) |
| | 877 | OVER ( |
| | 878 | PARTITION BY kniga_id |
| | 879 | ORDER BY mesec |
| | 880 | ) |
| | 881 | (H3) |
| | 882 | }}} |
| | 883 | |
| | 884 | Записите се рангираат обратно хронолошки: |
| | 885 | |
| | 886 | {{{ |
| | 887 | H5 ← |
| | 888 | ROW_NUMBER() |
| | 889 | OVER ( |
| | 890 | PARTITION BY kniga_id |
| | 891 | ORDER BY mesec DESC |
| | 892 | ) |
| | 893 | (H4) |
| | 894 | }}} |
| | 895 | |
| | 896 | Се задржува најновиот достапен период за секоја книга: |
| | 897 | |
| | 898 | {{{ |
| | 899 | LATEST ← σ rn=1 (H5) |
| | 900 | }}} |
| | 901 | |
| | 902 | Се додава моменталната залиха: |
| | 903 | |
| | 904 | {{{ |
| | 905 | RB ← |
| | 906 | LATEST |
| | 907 | ⋈ LATEST.kniga_id = KNIGI.kniga_id |
| | 908 | KNIGI |
| | 909 | }}} |
| | 910 | |
| | 911 | Се пресметуваат промената на продажбата и проценетото време до исцрпување: |
| | 912 | |
| | 913 | {{{ |
| | 914 | RA ← π |
| | 915 | kniga_id, |
| | 916 | naslov, |
| | 917 | mesec, |
| | 918 | kolicina_na_zaliha, |
| | 919 | prodadeni, |
| | 920 | prodadeni_prethoden_mesec, |
| | 921 | prihod, |
| | 922 | prihod_prethoden_mesec, |
| | 923 | broj_naracki, |
| | 924 | podvizen_prosek_prodazba, |
| | 925 | kumulativen_prihod, |
| | 926 | |
| | 927 | ( |
| | 928 | 100 × |
| | 929 | ( |
| | 930 | prodadeni - |
| | 931 | prodadeni_prethoden_mesec |
| | 932 | ) |
| | 933 | / |
| | 934 | prodadeni_prethoden_mesec |
| | 935 | ) |
| | 936 | → rast_prodazba_procent, |
| | 937 | |
| | 938 | ( |
| | 939 | kolicina_na_zaliha / |
| | 940 | podvizen_prosek_prodazba |
| | 941 | ) |
| | 942 | → proceneti_meseci_do_istekuvanje |
| | 943 | |
| | 944 | (RB) |
| | 945 | }}} |
| | 946 | |
| | 947 | Со условна проширена проекција се определува нивото на ризик. |
| | 948 | |
| | 949 | Потоа книгите се рангираат според проценетото време до исцрпување: |
| | 950 | |
| | 951 | {{{ |
| | 952 | RR ← |
| | 953 | DENSE_RANK() |
| | 954 | OVER ( |
| | 955 | ORDER BY |
| | 956 | proceneti_meseci_do_istekuvanje ASC |
| | 957 | ) |
| | 958 | (RA) |
| | 959 | }}} |
| | 960 | |
| | 961 | Конечниот резултат е: |
| | 962 | |
| | 963 | {{{ |
| | 964 | RESULT ← |
| | 965 | τ rang_rizik ASC, |
| | 966 | naslov ASC |
| | 967 | (RR) |
| | 968 | }}} |
| | 969 | |
| | 970 | |
| | 971 | === Објаснување === |
| | 972 | |
| | 973 | Во ''monthly_book_sales'' реализираните продажби се агрегираат на месечно ниво. |
| | 974 | |
| | 975 | Со: |
| | 976 | |
| | 977 | {{{ |
| | 978 | DATE_TRUNC('month', n.datum) |
| | 979 | }}} |
| | 980 | |
| | 981 | се формира временска серија за секоја книга. |
| | 982 | |
| | 983 | За секој достапен месечен период се пресметуваат бројот на продадени примероци, приходот и бројот на различни реализирани нарачки. |
| | 984 | |
| | 985 | Во ''sales_with_history'' се користи ''LAG()'' за добивање на продажбата и приходот од претходниот достапен период. |
| | 986 | |
| | 987 | Дополнително се пресметува подвижен просек: |
| | 988 | |
| | 989 | {{{ |
| | 990 | AVG(prodadeni) OVER ( |
| | 991 | PARTITION BY kniga_id |
| | 992 | ORDER BY mesec |
| | 993 | ROWS BETWEEN 2 PRECEDING AND CURRENT ROW |
| | 994 | ) |
| | 995 | }}} |
| | 996 | |
| | 997 | Овој показател ја зема предвид продажбата од тековниот и најмногу двата претходни достапни месечни записи. |
| | 998 | |
| | 999 | Поради тоа проценката на залихата е помалку зависна од евентуално невообичаено висока или ниска продажба во само еден период. |
| | 1000 | |
| | 1001 | Со: |
| | 1002 | |
| | 1003 | {{{ |
| | 1004 | SUM(prihod) OVER ( |
| | 1005 | PARTITION BY kniga_id |
| | 1006 | ORDER BY mesec |
| | 1007 | ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW |
| | 1008 | ) |
| | 1009 | }}} |
| | 1010 | |
| | 1011 | се добива кумулативниот приход. |
| | 1012 | |
| | 1013 | Во ''latest_period'' со ''ROW_NUMBER()'' се идентификува последниот достапен период за секоја книга. |
| | 1014 | |
| | 1015 | Потоа резултатот се поврзува со ''knigi'' за да се добие моменталната залиха. |
| | 1016 | |
| | 1017 | Проценетото време до исцрпување се пресметува како однос меѓу моменталната залиха и подвижниот просек на продажбата. |
| | 1018 | |
| | 1019 | ''NULLIF'' спречува делење со нула. |
| | 1020 | |
| | 1021 | Со ''CASE'' книгите се класифицираат во различни нивоа на ризик, а со ''DENSE_RANK()'' се рангираат според проценетата итност за дополнување на залихата. |
| | 1022 | |
| | 1023 | Важно е дека ''LAG()'' го враќа претходниот достапен месечен запис. Ако во одреден календарски месец нема реализирана продажба за книгата, тој месец не се појавува како посебен ред со продажба 0. |
| | 1024 | |