Changes between Version 4 and Version 5 of AdvancedReports


Ignore:
Timestamp:
09/30/26 17:14:46 (112 minutes ago)
Author:
223306
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • AdvancedReports

    v4 v5  
    1 = Напредни извештаи =
    2 
    3 Извештаите се извршуваат над податоците од базата на податоци на !BiblioPremium. Извештаите користат податоци од повеќе релации, спојувања, агрегатни функции и пресметани вредности за добивање аналитички информации за продажбата и залихите.
    4 
    5 Во релационата алгебра се користи проширена нотација: σ (селекција), π (проекција), ⋈ (спојување), γ (групирање и агрегатни функции), τ (подредување) и ← (доделување на привремена релација).
    6 
    7 == Најуспешни книги според продажба и приход ==
     1= Advanced Reports =
     2
     3Во оваа фаза се изработени два комплексни аналитички извештаи над податоците од базата на !BiblioPremium.
     4
     5Извештаите се наменети за долгорочна анализа на работењето на системот и користат податоци од повеќе релации, повеќестепено агрегирање, временска анализа, CTE (Common Table Expressions), прозорски функции, рангирање и пресметани аналитички показатели.
     6
     7Во двата извештаи се земаат предвид само реализираните нарачки со статус ''ZAVRSENA''.
     8
     9Во релационата алгебра се користи проширена нотација:
     10
     11* σ – селекција
     12* π – генерализирана проекција со пресметани атрибути
     13* ⋈ – спојување
     14* ⟕ – лево надворешно спојување
     15* ρ – преименување
     16* γ – групирање и агрегатни функции
     17* τ – подредување
     18* ← – доделување на привремена релација
     19
     20За аналитичките операции како RANK, DENSE_RANK, LAG, ROW_NUMBER и подвижни агрегати се користи проширена аналитичка нотација.
     21
     22
     23== Квартална анализа и рангирање на продажните перформанси на книгите по категории ==
    824
    925=== Data requirements description ===
    1026
    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{{{
     52WITH 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
     98category_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
     124quarterly_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
     176final_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
    27225SELECT
    28     k.kniga_id,
    29     k.naslov,
    30     k.isbn,
    31     SUM(s.kolicina) AS vkupno_prodadeni,
    32     SUM(s.kolicina * s.edinechna_cena) AS vkupen_prihod,
    33     COUNT(DISTINCT n.naracka_id) AS broj_naracki,
     226    TO_CHAR(
     227        kvartal,
     228        'YYYY-"Q"Q'
     229    ) AS kvartal,
     230
     231    kategorija,
     232    kniga_id,
     233    naslov,
     234    isbn,
     235
     236    broj_naracki,
     237    vkupno_prodadeni,
     238
    34239    ROUND(
    35         AVG(s.kolicina * s.edinechna_cena),
     240        vkupen_prihod,
    36241        2
    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,
    120243
    121244    ROUND(
    122         SUM(s.kolicina)::numeric /
    123         NULLIF(COUNT(DISTINCT n.naracka_id), 0),
     245        prosecna_prodazna_cena,
    124246        2
    125     ) AS prosecna_prodazba_po_naracka,
     247    ) AS prosecna_prodazna_cena,
     248
     249    broj_prodavani_knigi,
     250
     251    vkupno_prodadeni_kategorija,
    126252
    127253    ROUND(
    128         k.kolicina_na_zaliha::numeric /
    129         NULLIF(
    130             SUM(s.kolicina)::numeric /
    131             NULLIF(COUNT(DISTINCT n.naracka_id), 0),
     254        vkupen_prihod_kategorija,
     255        2
     256    ) AS vkupen_prihod_kategorija,
     257
     258    ROUND(
     259        prosechen_prihod_po_kniga,
     260        2
     261    ) AS prosechen_prihod_po_kniga,
     262
     263    rang_vo_kategorija,
     264
     265    procent_od_prihod_na_kategorija,
     266
     267    ROUND(
     268        COALESCE(
     269            prihod_prethoden_kvartal,
    132270            0
    133271        ),
    134272        2
    135     ) AS proceneti_naracki_do_istekuvanje
    136 
    137 FROM project.knigi k
    138 
    139 JOIN project.sodrzi s
    140     ON k.kniga_id = s.kniga_id
    141 
    142 JOIN project.naracki n
    143     ON s.naracka_id = n.naracka_id
    144     AND n.status = 'ZAVRSENA'
    145 
    146 GROUP BY
    147     k.kniga_id,
    148     k.naslov,
    149     k.kolicina_na_zaliha
    150 
    151 HAVING SUM(s.kolicina) > 0
     273    ) AS prihod_prethoden_kvartal,
     274
     275    promena_prihod_procent,
     276
     277    ROUND(
     278        kumulativen_prihod,
     279        2
     280    ) AS kumulativen_prihod,
     281
     282    trend
     283
     284FROM final_analysis
    152285
    153286ORDER BY
    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{{{
     299N ← σ status='ZAVRSENA' (NARACKI)
     300}}}
     301
     302Книгите се поврзуваат со категориите:
     303
     304{{{
     305KC ←
     306KNIGI
     307⋈ KNIGI.kniga_id = IMA_KATEGORIJA.kniga_id
     308IMA_KATEGORIJA
     309⋈ IMA_KATEGORIJA.kategorija_id = KATEGORII.kategorija_id
     310KATEGORII
     311}}}
     312
     313Потоа се додаваат ставките и реализираните нарачки:
     314
     315{{{
     316R1 ←
     317KC
     318⋈ KNIGI.kniga_id = SODRZI.kniga_id
     319SODRZI
     320
     321R2 ←
     322R1
     323⋈ SODRZI.naracka_id = N.naracka_id
     324N
     325}}}
     326
     327Датумот се трансформира во квартал и се врши првото ниво на агрегација:
     328
     329{{{
     330QBS ←
     331quarter(datum),
     332kniga_id,
     333naslov,
     334isbn,
     335kategorija_id,
     336kategorija
     337
     338γ
     339
     340COUNT-DISTINCT(naracka_id)
     341    → broj_naracki,
     342
     343SUM(kolicina)
     344    → vkupno_prodadeni,
     345
     346SUM(kolicina × edinechna_cena)
     347    → vkupen_prihod,
     348
     349AVG(edinechna_cena)
     350    → prosecna_prodazna_cena
     351
     352(R2)
     353}}}
     354
     355Второто ниво на агрегација се врши на ниво квартал–категорија:
     356
     357{{{
     358CS ←
     359kvartal,
     360kategorija_id,
     361kategorija
     362
     363γ
     364
     365COUNT-DISTINCT(kniga_id)
     366    → broj_prodavani_knigi,
     367
     368SUM(vkupno_prodadeni)
     369    → vkupno_prodadeni_kategorija,
     370
     371SUM(vkupen_prihod)
     372    → vkupen_prihod_kategorija,
     373
     374AVG(vkupen_prihod)
     375    → prosechen_prihod_po_kniga
     376
     377(QBS)
     378}}}
     379
     380Двете нивоа на анализа се поврзуваат:
     381
     382{{{
     383QA ←
     384QBS
     385⋈ QBS.kvartal = CS.kvartal
     386  ∧ QBS.kategorija_id = CS.kategorija_id
     387CS
     388}}}
     389
     390Рангирањето во рамки на квартал и категорија се изразува со проширена аналитичка операција:
     391
     392{{{
     393QA1 ←
     394RANK()
     395OVER (
     396    PARTITION BY kvartal, kategorija_id
     397    ORDER BY vkupen_prihod DESC
     398)
     399(QA)
     400}}}
     401
     402Приходот од претходниот достапен квартален запис се добива со:
     403
     404{{{
     405QA2 ←
     406LAG(vkupen_prihod)
     407OVER (
     408    PARTITION BY kniga_id, kategorija_id
     409    ORDER BY kvartal
     410)
     411(QA1)
     412}}}
     413
     414Кумулативниот приход се добива со:
     415
     416{{{
     417QA3 ←
     418CUMULATIVE_SUM(vkupen_prihod)
     419OVER (
     420    PARTITION BY kniga_id, kategorija_id
     421    ORDER BY kvartal
     422)
     423(QA2)
     424}}}
     425
     426Се пресметуваат процентуалното учество и промената на приходот:
     427
     428{{{
     429FA ← π
     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{{{
     473RESULT ←
     474τ kvartal ASC,
     475  kategorija ASC,
     476  rang_vo_kategorija ASC
     477(FA)
     478}}}
     479
    191480
    192481=== Објаснување ===
    193482
    194 Прво се спојуваат книгите со ставките од нарачките, а потоа со нарачките. Се задржуваат само реализираните нарачки со статус ''ZAVRSENA''.
    195 
    196 Со γ се групираат податоците според книга и се пресметуваат вкупниот број на продадени примероци и бројот на различни реализирани нарачки.
    197 
    198 Просечната продажба по нарачка се добива со делење на вкупниот број на продадени примероци со бројот на реализирани нарачки.
    199 
    200 Проценетиот број на нарачки до исцрпување на залихата се добива со делење на моменталната залиха со просечната продажба по нарачка.
    201 
    202 Со τ резултатите се подредуваат растечки според проценетиот број на нарачки до исцрпување на залихата.
     483Извештајот користи повеќе нивоа на обработка.
     484
     485Во ''quarterly_book_sales'' податоците од пет релации се поврзуваат и се агрегираат на ниво квартал–книга–категорија.
     486
     487Со:
     488
     489{{{
     490DATE_TRUNC('quarter', n.datum)
     491}}}
     492
     493датумите на нарачките се претвораат во квартални периоди.
     494
     495За секоја книга се пресметуваат бројот на реализирани нарачки, бројот на продадени примероци, приходот и просечната продажна цена.
     496
     497Во ''category_statistics'' се врши второ ниво на агрегација. Податоците кои претходно биле пресметани по книга повторно се агрегираат на ниво квартал–категорија.
     498
     499На овој начин може да се спореди индивидуалниот резултат на книгата со резултатите на целата категорија.
     500
     501Со:
     502
     503{{{
     504RANK() 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{{{
     554WITH 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
     596sales_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
     634latest_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
     646risk_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
     703final_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
     733SELECT
     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
     781FROM final_analysis
     782
     783ORDER BY
     784    rang_rizik,
     785    naslov;
     786}}}
     787
     788
     789=== Solution Relational Algebra ===
     790
     791Најпрво се издвојуваат реализираните нарачки:
     792
     793{{{
     794N ← σ status='ZAVRSENA' (NARACKI)
     795}}}
     796
     797Се поврзуваат книгите, ставките и реализираните нарачки:
     798
     799{{{
     800R1 ←
     801KNIGI
     802⋈ KNIGI.kniga_id = SODRZI.kniga_id
     803SODRZI
     804
     805R2 ←
     806R1
     807⋈ SODRZI.naracka_id = N.naracka_id
     808N
     809}}}
     810
     811Податоците се агрегираат на ниво книга–месец:
     812
     813{{{
     814MBS ←
     815kniga_id,
     816naslov,
     817month(datum)→mesec
     818
     819γ
     820
     821SUM(kolicina)
     822    → prodadeni,
     823
     824SUM(kolicina × edinechna_cena)
     825    → prihod,
     826
     827COUNT-DISTINCT(naracka_id)
     828    → broj_naracki
     829
     830(R2)
     831}}}
     832
     833Продажбата од претходниот достапен период се добива со:
     834
     835{{{
     836H1 ←
     837LAG(prodadeni)
     838OVER (
     839    PARTITION BY kniga_id
     840    ORDER BY mesec
     841)
     842(MBS)
     843}}}
     844
     845Приходот од претходниот достапен период се добива со:
     846
     847{{{
     848H2 ←
     849LAG(prihod)
     850OVER (
     851    PARTITION BY kniga_id
     852    ORDER BY mesec
     853)
     854(H1)
     855}}}
     856
     857Подвижниот просек се пресметува над тековниот и најмногу двата претходни достапни месечни записи:
     858
     859{{{
     860H3 ←
     861MOVING_AVG(prodadeni)
     862OVER (
     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{{{
     875H4 ←
     876CUMULATIVE_SUM(prihod)
     877OVER (
     878    PARTITION BY kniga_id
     879    ORDER BY mesec
     880)
     881(H3)
     882}}}
     883
     884Записите се рангираат обратно хронолошки:
     885
     886{{{
     887H5 ←
     888ROW_NUMBER()
     889OVER (
     890    PARTITION BY kniga_id
     891    ORDER BY mesec DESC
     892)
     893(H4)
     894}}}
     895
     896Се задржува најновиот достапен период за секоја книга:
     897
     898{{{
     899LATEST ← σ rn=1 (H5)
     900}}}
     901
     902Се додава моменталната залиха:
     903
     904{{{
     905RB ←
     906LATEST
     907⋈ LATEST.kniga_id = KNIGI.kniga_id
     908KNIGI
     909}}}
     910
     911Се пресметуваат промената на продажбата и проценетото време до исцрпување:
     912
     913{{{
     914RA ← π
     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{{{
     952RR ←
     953DENSE_RANK()
     954OVER (
     955    ORDER BY
     956    proceneti_meseci_do_istekuvanje ASC
     957)
     958(RA)
     959}}}
     960
     961Конечниот резултат е:
     962
     963{{{
     964RESULT ←
     965τ rang_rizik ASC,
     966  naslov ASC
     967(RR)
     968}}}
     969
     970
     971=== Објаснување ===
     972
     973Во ''monthly_book_sales'' реализираните продажби се агрегираат на месечно ниво.
     974
     975Со:
     976
     977{{{
     978DATE_TRUNC('month', n.datum)
     979}}}
     980
     981се формира временска серија за секоја книга.
     982
     983За секој достапен месечен период се пресметуваат бројот на продадени примероци, приходот и бројот на различни реализирани нарачки.
     984
     985Во ''sales_with_history'' се користи ''LAG()'' за добивање на продажбата и приходот од претходниот достапен период.
     986
     987Дополнително се пресметува подвижен просек:
     988
     989{{{
     990AVG(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{{{
     1004SUM(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
    2031025
    2041026== Финална дискусија ==
    2051027
    206 Двата извештаи користат податоци од повеќе релации и агрегатни функции за добивање аналитички информации за работењето на !BiblioPremium.
    207 
    208 Првиот извештај се фокусира на продажбата и приходите и овозможува анализа на успешноста на различните книги според бројот на продадени примероци и остварениот приход.
    209 
    210 Вториот извештај се фокусира на залихите и продажната динамика и овозможува идентификување на книги кај кои постои потенцијален ризик од исцрпување на залихата.
    211 
    212 И двата извештаи ги користат само нарачките со статус ''ZAVRSENA'', со што незавршените нарачки не влијаат врз аналитичките пресметки.
    213 
    214 Извештаите можат да се користат за периодична анализа на продажбата, приходите и залихите во !BiblioPremium.
     1028Двата извештаи се наменети за долгорочна аналитичка обработка на податоците во !BiblioPremium.
     1029
     1030Првиот извештај се фокусира на кварталните продажни перформанси. Тој комбинира анализа на книга, категорија и временски период и овозможува споредување на тековниот резултат со претходните периоди.
     1031
     1032Со него може да се идентификуваат книгите кои генерираат најголем дел од приходот на категоријата, да се следи нивниот ранг и да се анализира дали приходот расте, опаѓа или останува релативно стабилен.
     1033
     1034Вториот извештај има прогнозна цел. Историската продажба се анализира како временска серија и се користи за проценка на ризикот од исцрпување на моменталната залиха.
     1035
     1036Во двата извештаи се користат повеќе напредни SQL механизми:
     1037
     1038* CTE (Common Table Expressions);
     1039* повеќекратни JOIN операции;
     1040* повеќестепено агрегирање;
     1041* COUNT(DISTINCT ...);
     1042* SUM и AVG;
     1043* DATE_TRUNC;
     1044* RANK();
     1045* DENSE_RANK();
     1046* LAG();
     1047* ROW_NUMBER();
     1048* PARTITION BY;
     1049* прозорски функции;
     1050* window frame со ROWS BETWEEN;
     1051* кумулативни пресметки;
     1052* подвижен просек;
     1053* CASE;
     1054* NULLIF;
     1055* COALESCE;
     1056* процентуални показатели;
     1057* квартална анализа;
     1058* месечна анализа;
     1059* споредување со претходни периоди;
     1060* рангирање;
     1061* проценка на идно исцрпување на залиха.
     1062
     1063Со тоа извештаите не прикажуваат само податоци кои директно се наоѓаат во базата, туку од постојните трансакциски податоци изведуваат дополнителни информации за долгорочните продажни перформанси и идните потреби за залиха.