Changes between Initial Version and Version 1 of Напредна тема


Ignore:
Timestamp:
06/14/26 00:24:32 (5 days ago)
Author:
231124
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • Напредна тема

    v1 v1  
     1= DATA CUBE =
     2= BankPaymentService =
     3
     4Напредни Бази на Податоци — Проектна Документација
     5
     6||= '''Предмет''' =|| Напредни Бази на Податоци ||
     7||= '''Тема''' =|| Data Cube за банкарска аналитика ||
     8||= '''Технологија''' =|| PostgreSQL · Star Schema · OLAP ||
     9||= '''Опфат''' =|| Трансакции · Кредити · AML · Ризик · Перформанси ||
     10
     11== 1. Вовед — Цел и мотивација ==
     12
     13Data Cube претставува аналитички модел кој овозможува повеќедимензионална анализа на податоци. Наместо за секој извештај да се пишуваат посебни SQL барања, Data Cube овозможува со едно барање да се добијат агрегирани резултати по различни димензии, како што се време, филијала, валута или нивни комбинации.
     14
     15Во банкарски систем со голем обем на трансакции, ваквиот пристап значително ја забрзува анализата и овозможува поефикасно донесување одлуки базирани на податоци.
     16
     17Со Data Cube може лесно да се одговорат прашања како:
     18
     19 * Колкав бил прометот во EUR во одреден регион и период?
     20 * Кој тип на кредит има највисок ризик од неплаќање?
     21 * Кои филијали имаат најголем придонес во вкупниот ризик?
     22 * Какви трендови се забележуваат на месечно и квартално ниво?
     23
     24Data Cube не го менува начинот на складирање на податоците, туку начинот на нивна анализа.
     25
     26== 2. Архитектура — Star Schema ==
     27
     28Решението е имплементирано со Star Schema архитектура. Во центарот се наоѓаат факт-табелите кои ги содржат главните деловни настани, додека околу нив се поставени димензионалните табели кои даваат контекст за анализата.
     29
     30Овој модел е широко користен во аналитички системи бидејќи нуди едноставна структура, добри перформанси и лесна интерпретација на податоците.
     31
     32||= '''Компонента''' =||= '''Улога''' =||= '''Примери''' =||
     33|| Dimension Tables || Описни / контекстуални податоци || dim_time, dim_branch, dim_currency, dim_loan_type ||
     34|| Fact Tables || Мерливи деловни настани || fact_transaction, fact_loan, fact_installment ||
     35|| Summary Tables || Претходно агрегирани резултати || summary_monthly_transactions, summary_quarterly_loans ||
     36
     37=== 2.1 Димензионални табели ===
     38
     39==== dim_time — Временска димензија ====
     40
     41Содржи по еден запис за секој ден (5 години наназад, 2 години однапред). Вклучува атрибути за ден, недела, месец, квартал и година, генерирани преку generate_series. ON CONFLICT DO NOTHING обезбедува безбедно повторно извршување.
     42
     43{{{
     44CREATE TABLE IF NOT EXISTS dim_time (
     45    time_id       SERIAL PRIMARY KEY,
     46    full_date     DATE        NOT NULL UNIQUE,
     47    day_of_week   INT         NOT NULL,  -- 1=Пон … 7=Нед (ISO)
     48    quarter_num   INT         NOT NULL,  -- 1–4
     49    quarter_label VARCHAR(10) NOT NULL,  -- 'Q1 2026'
     50    year_num      INT         NOT NULL,
     51    is_weekend    BOOLEAN     NOT NULL,
     52    is_holiday    BOOLEAN     NOT NULL DEFAULT FALSE
     53);
     54
     55INSERT INTO dim_time (...)
     56SELECT d, EXTRACT(ISODOW FROM d)::INT, ...,
     57       'Q' || EXTRACT(QUARTER FROM d) || ' ' || EXTRACT(YEAR FROM d)
     58FROM generate_series(
     59    CURRENT_DATE - INTERVAL '5 years',
     60    CURRENT_DATE + INTERVAL '2 years',
     61    INTERVAL '1 day') AS t(d)
     62ON CONFLICT (full_date) DO NOTHING;
     63}}}
     64
     65==== dim_branch — Филијали со регион и категорија ====
     66
     67Секоја филијала се класифицира по регион (Sever/Centar/Jug) и по големина (LARGE/MEDIUM/SMALL) врз основа на бројот на активни вработени.
     68
     69{{{
     70INSERT INTO dim_branch (branch_id, branch_name, city, region, branch_size)
     71SELECT b.branch_id, b.branch_name, b.city,
     72       CASE b.city
     73           WHEN 'Skopje' THEN 'Centar'
     74           WHEN 'Tetovo' THEN 'Sever'
     75           WHEN 'Bitola' THEN 'Jug'  ...
     76       END AS region,
     77       CASE WHEN emp_count > 20 THEN 'LARGE'
     78            WHEN emp_count > 10 THEN 'MEDIUM'
     79            ELSE 'SMALL'
     80       END AS branch_size
     81FROM Branch b LEFT JOIN (...) e ON b.branch_id = e.branch_id;
     82}}}
     83
     84Покрај овие димензионални табели, искористена е и '''dim_loan_type''' (четири типа кредити, секој со своја ризична категорија и типичен рок).
     85
     86||= '''Код''' =||= '''Назив''' =||= '''Ризик''' =||= '''Типичен рок''' =||
     87|| HOME || Станбен кредит || LOW — низок || 240 месеци (20 год.) ||
     88|| CAR || Автомобилски кредит || MEDIUM — среден || 60 месеци (5 год.) ||
     89|| PERSONAL || Личен кредит || HIGH — висок || 36 месеци (3 год.) ||
     90|| BUSINESS || Деловен кредит || MEDIUM — среден || 120 месеци (10 год.) ||
     91
     92Димензионалната табела '''dim_transaction_type''' ги дефинира различните типови на банкарски трансакции (на пример депозит, повлекување, плаќање, трансфер). Содржи информации за категоријата на трансакцијата и дали таа влијае врз состојбата на сметката, што овозможува анализа на приливи, одливи и други активности.
     93
     94Димензионалната табела '''dim_currency''' ги содржи валутите што се поддржани во системот и ги групира според географски регион. На тој начин може да се анализираат трансакциите на регионално ниво, без потреба секоја валута да се разгледува одделно.
     95
     96||= '''Регион''' =||= '''Примери''' =||
     97|| EUROPE || EUR, MKD, GBP, CHF, TRY, RSD, PLN, SEK... ||
     98|| AMERICA || USD, CAD, BRL, MXN, ARS, COP... ||
     99|| ASIA || JPY, CNY, INR, SGD, AED, SAR, KRW, HKD... ||
     100|| AFRICA || ZAR, EGP, KES, NGN, GHS, MAD... ||
     101|| OTHER || Океанија и останати ||
     102
     103== 3. Факт-табели (Fact Tables) ==
     104
     105Факт-табелите ги содржат настаните кои се предмет на анализа и ги поврзуваат мерливите показатели со димензиите. Секој запис претставува конкретен настан: трансакција, кредит или рата.
     106
     107=== 3.1 fact_transaction — EUR конверзија и детектирање на сомнителни трансакции ===
     108
     109==== Конверзија во EUR преку LATERAL ====
     110
     111За да се обезбеди конзистентна анализа, секоја трансакција се конвертира во EUR во моментот на вчитување во Data Cube. Конверзијата се врши со користење на последниот достапен девизен курс кој е валиден на датумот на трансакцијата.
     112
     113Конвертираниот износ се пресметува со формулата: `amount_eur = amount / rate`
     114
     115'''Детектирање на сомнителни трансакции'''
     116
     117Овој пристап овозможува рана идентификација на потенцијално ризични трансакции и обезбедува основа за понатамошна анализа од страна на тимовите за ризик и усогласеност.
     118
     119{{{
     120INSERT INTO fact_transaction (..., amount_eur, is_suspicious, ...)
     121SELECT ...,
     122    CASE WHEN er.rate IS NOT NULL
     123         THEN ROUND(t.amount / er.rate, 2)
     124         ELSE NULL
     125    END AS amount_eur,
     126    -- AML: >9000 EUR + статус PENDING/FAILED
     127    CASE WHEN er.rate IS NOT NULL
     128              AND (t.amount / er.rate) > 9000
     129              AND t.status IN ('PENDING','FAILED')
     130         THEN TRUE ELSE FALSE
     131    END AS is_suspicious
     132FROM Transaction t
     133LEFT JOIN LATERAL (
     134    SELECT rate FROM Exchange_rate
     135    WHERE currency_id = t.currency_id
     136      AND rate_date <= t.transaction_date
     137    ORDER BY rate_date DESC LIMIT 1
     138) er ON TRUE;
     139}}}
     140
     141==== Индекси за оптимизација ====
     142
     143Поради големиот обем на податоци, врз табелата се дефинирани повеќе индекси со цел подобрување на перформансите на аналитичките барања.
     144
     145{{{
     146CREATE INDEX idx_ft_time       ON fact_transaction(time_id);
     147CREATE INDEX idx_ft_branch     ON fact_transaction(branch_dim_id);
     148CREATE INDEX idx_ft_currency   ON fact_transaction(currency_dim_id);
     149-- Partial index: само сомнителни трансакции (AML)
     150}}}
     151
     152=== 3.3 fact_installment ===
     153
     154Секој ред во табелата претставува една рата од кредит. Табелата содржи информации за статусот на ратите и овозможува следење на навременоста на плаќањата, вклучувајќи ги и ратите со задоцнување.
     155
     156{{{
     157CREATE TABLE IF NOT EXISTS fact_installment (
     158    fact_inst_id    BIGSERIAL PRIMARY KEY,
     159    time_id_due     INT   NOT NULL REFERENCES dim_time(time_id), -- кога ТРЕБА да се плати
     160    time_id_paid    INT   REFERENCES dim_time(time_id),          -- кога НАВИСТИНА е платена
     161    loan_type_id    INT   REFERENCES dim_loan_type(loan_type_id),
     162    branch_dim_id   INT   REFERENCES dim_branch(branch_dim_id),
     163    installment_id  BIGINT NOT NULL,
     164    loan_id         BIGINT NOT NULL,
     165    client_id       INT,
     166    installment_num INT,
     167    status          VARCHAR(20),
     168    amount          DECIMAL(15,2) NOT NULL,
     169    days_late       INT,     -- NULL ако не е платена; позитивен ако paid > due
     170    is_paid         BOOLEAN NOT NULL DEFAULT FALSE,
     171    is_late         BOOLEAN NOT NULL DEFAULT FALSE,
     172    is_pending      BOOLEAN NOT NULL DEFAULT FALSE
     173);
     174
     175-- days_late логика:
     176CASE
     177    WHEN li.paid_date IS NOT NULL AND li.paid_date > li.due_date
     178        THEN (li.paid_date - li.due_date)
     179    ELSE NULL   -- не измислуваме вредност ако не е платена уште
     180END AS days_late
     181}}}
     182
     183 * `is_defaulted` — TRUE ако постои барем една LATE рата во loan_installment.
     184
     185== 4. Аналитички барања — CUBE, ROLLUP, GROUPING SETS ==
     186
     187Една од клучните цели е овозможување брза и флексибилна анализа на податоците со OLAP техники. Наместо креирање посебни извештаи, Data Cube овозможува повеќедимензионални агрегати со едно SQL барање. Со користење на CUBE, ROLLUP и GROUPING SETS се добиваат извештаи на различни нивоа на деталност без дополнителна обработка, што ја забрзува анализата и ја зголемува флексибилноста при донесување одлуки.
     188
     189=== 4.1 CUBE — Трансакции по 4 димензии (16 комбинации) ===
     190
     191Ова барање овозможува анализа на вкупниот промет според:
     192
     193 * година
     194 * квартал
     195 * филијала
     196 * валута
     197
     198Со користење на CUBE, се генерираат сите можни комбинации на агрегација помеѓу наведените димензии.
     199
     200Како резултат се добиваат:
     201
     202 * детални записи за секоја комбинација
     203 * меѓузбирови по година
     204 * меѓузбирови по филијала
     205 * меѓузбирови по валута
     206 * вкупен агрегат за целиот систем
     207
     208Овој извештај овозможува истовремено следење на трендовите од различни временски и организациски перспективи.
     209
     210{{{
     211SELECT
     212    dt.year_num, dt.quarter_label,
     213    db.branch_name, dtt.type_name,
     214    COUNT(*)                  AS transaction_count,
     215    SUM(ft.amount_eur)        AS total_amount_eur,
     216    ROUND(AVG(ft.amount_eur),2) AS avg_amount_eur,
     217    SUM(ft.is_suspicious::INT)  AS suspicious_count,
     218    ROUND(SUM(ft.is_completed::INT)::NUMERIC
     219          / NULLIF(COUNT(*),0)*100,2) AS completion_rate_pct,
     220    GROUPING(dt.year_num)      AS grp_year,
     221    GROUPING(db.branch_name)   AS grp_branch
     222FROM fact_transaction ft
     223JOIN dim_time dt  ON ft.time_id = dt.time_id
     224JOIN dim_branch db ON ft.branch_dim_id = db.branch_dim_id
     225JOIN dim_transaction_type dtt ON ft.trx_type_dim_id = dtt.trx_type_dim_id
     226GROUP BY CUBE(dt.year_num, dt.quarter_label, db.branch_name, dtt.type_name)
     227ORDER BY dt.year_num NULLS LAST, dt.quarter_label NULLS LAST;
     228}}}
     229
     230=== 4.2 ROLLUP — Хиерархиска временска анализа ===
     231
     232За разлика од CUBE, кој генерира сите можни комбинации, ROLLUP создава хиерархиски нивоа на агрегација (Година → Квартал → Месец). Овој пристап е особено корисен кога податоците природно следат временска или организациска хиерархија.
     233
     234{{{
     235-- rollup_level: 3=Grand Total, 2=по Година, 1=по Квартал, 0=по Месец
     236SELECT dt.year_num, dt.quarter_label, dt.month_num,
     237    COUNT(*)           AS total_transactions,
     238    SUM(ft.amount_eur) AS total_amount_eur,
     239    GROUPING(dt.year_num) + GROUPING(dt.quarter_num)
     240    + GROUPING(dt.month_num) AS rollup_level
     241FROM fact_transaction ft
     242JOIN dim_time dt ON ft.time_id = dt.time_id
     243GROUP BY ROLLUP(dt.year_num, (dt.quarter_num, dt.quarter_label), dt.month_num);
     244}}}
     245
     246=== 4.3 GROUPING SETS — Мулти-перспективна анализа ===
     247
     248Во практични сценарија не е секогаш потребно да се пресметаат сите комбинации што ги генерира CUBE. Наместо тоа се користи GROUPING SETS, кој овозможува експлицитно дефинирање само на потребните агрегации. Овој пристап обезбедува:
     249
     250 * подобри перформанси
     251 * помала потрошувачка на ресурси
     252 * пофокусирани и појасни резултати
     253
     254Во системот се дефинираат клучни аналитички перспективи како анализа по година и филијала, година и валута, регион, тип на кредит, како и глобални агрегати. Со GROUPING SETS се пресметуваат само овие релевантни комбинации, без непотребни пресметки.
     255
     256{{{
     257GROUP BY GROUPING SETS (
     258    (dt.quarter_label, dtt.type_name),           -- Per tip i kvartal
     259    (dt.quarter_label, db.branch_name, db.region), -- Per filijala
     260    (dt.quarter_label),                           -- Vkupno po kvartal
     261    (db.branch_name, db.region)                   -- Vkupno po filijala
     262)
     263}}}
     264
     265== 5. AML — Детектирање на сомнителни трансакции ==
     266
     267Системот поддржува процеси за спречување перење пари (Anti-Money Laundering). Трансакција се означува како сомнителна (`is_suspicious = TRUE`) доколку нејзиниот EUR еквивалент надминува 9.000 EUR (стандарден ЕУ AML праг) и статусот е PENDING или FAILED.
     268
     269{{{
     270-- AML CUBE: По ГОДИНА × КВАРТАЛ × ТИП × ФИЛИЈАЛА
     271SELECT dt.year_num, dt.quarter_label,
     272    dtt.type_name, db.branch_name, db.region,
     273    COUNT(*)                             AS total_txns,
     274    SUM(ft.is_suspicious::INT)           AS suspicious_count,
     275    ROUND(SUM(CASE WHEN ft.is_suspicious
     276        THEN ft.amount_eur ELSE 0 END),2) AS suspicious_eur,
     277    ROUND(SUM(ft.is_suspicious::INT)::NUMERIC
     278        / NULLIF(COUNT(*),0)*100,2)       AS suspicious_rate_pct,
     279    GROUPING(dt.year_num)   AS grp_year,
     280    GROUPING(db.branch_name) AS grp_branch
     281FROM fact_transaction ft
     282JOIN dim_time dt ...
     283GROUP BY CUBE(dt.year_num, dt.quarter_label,
     284              (dtt.type_name, dtt.category), (db.branch_name, db.region))
     285HAVING SUM(ft.is_suspicious::INT) > 0  -- само ќелии со AML активност
     286ORDER BY suspicious_count DESC NULLS LAST;
     287}}}
     288
     289Partial индексот `idx_ft_suspicious` ги опфаќа само записите каде `is_suspicious = TRUE`, со што се намалува големината на индексот и се зголемува брзината на AML барањата.
     290
     291== 6. Window Functions — Тренд анализа ==
     292
     293Window Functions се користат за извршување на пресметки врз група редови (window) без да се изгубат поединечните записи, за разлика од GROUP BY кој ги агрегира редовите. Тие овозможуваат анализа на трендови преку пресметување на:
     294
     295 * кумулативни суми (YTD)
     296 * споредба со претходни периоди (LAG за MoM анализа)
     297 * подвижни просеци (Moving Average)
     298 * рангирање (RANK)
     299
     300На овој начин може да се следи развојот на податоците низ времето додека деталните информации за секој период остануваат достапни.
     301
     302=== 6.1 Месечен тренд: YTD + MoM + Moving Average ===
     303
     304{{{
     305WITH monthly_agg AS (
     306    SELECT dt.year_num, dt.month_num,
     307        COUNT(*) AS txn_count, SUM(ft.amount_eur) AS total_eur
     308    FROM fact_transaction ft JOIN dim_time dt ON ft.time_id = dt.time_id
     309    GROUP BY dt.year_num, dt.month_num
     310)
     311SELECT year_num, month_num, txn_count,
     312    -- Year-to-Date кумулативна EUR сума
     313    SUM(total_eur) OVER(
     314        PARTITION BY year_num ORDER BY month_num
     315        ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
     316    ) AS ytd_cumulative_eur,
     317    -- Month-over-Month промена (%)
     318    ROUND((total_eur - LAG(total_eur) OVER(ORDER BY year_num, month_num))
     319        / NULLIF(LAG(total_eur) OVER(ORDER BY year_num, month_num),0)*100
     320    ,2) AS mom_eur_change_pct,
     321    -- 3-месечен подвижен просек
     322    ROUND(AVG(total_eur) OVER(
     323        ORDER BY year_num, month_num
     324        ROWS BETWEEN 2 PRECEDING AND CURRENT ROW
     325    ),2) AS moving_avg_3m_eur,
     326    -- Ранг на месецот во годината
     327    RANK() OVER(PARTITION BY year_num ORDER BY total_eur DESC) AS rank_in_year
     328FROM monthly_agg ORDER BY year_num, month_num;
     329}}}
     330
     331== 7. Summary Tables и Refresh Procedure ==
     332
     333За системи со голем обем на податоци, повторливото пресметување на сложени агрегати може да биде временски интензивно. Summary табелите ги складираат однапред пресметаните резултати, а refresh процедурата ги освежува периодично.
     334
     335||= '''Компонента''' =||= '''Улога''' =||= '''Примери''' =||
     336|| summary_monthly_transactions || Месечни агрегати по филијала/тип || txn_count, total_eur, suspicious_count ||
     337|| summary_quarterly_loans || Квартални кредитни агрегати || loan_count, default_rate_pct, avg_ltv ||
     338|| summary_quarterly_debt || Квартална статистика на доцнења || late_count, avg_days_late, amount_overdue ||
     339
     340=== 7.1 Refresh Procedure ===
     341
     342Процедурата `refresh_data_cube_summaries()` се извршува периодично во временски интервали кога системот е помалку оптоварен (пр. во ноќните часови), со цел да се минимизира влијанието врз оперативните активности. Освежувањето на секоја summary табела се реализира преку пристапот TRUNCATE + INSERT, при што постојните агрегирани податоци се заменуваат со новопресметани вредности. Овој пристап обезбедува конзистентност, точност и усогласеност на податоците со моменталната состојба во системот.
     343
     344{{{
     345CREATE OR REPLACE PROCEDURE refresh_data_cube_summaries()
     346LANGUAGE plpgsql AS $$
     347BEGIN
     348    -- 1. Месечни трансакции
     349    TRUNCATE summary_monthly_transactions;
     350    INSERT INTO summary_monthly_transactions (...)
     351    SELECT dt.year_num, dt.month_num, dt.quarter_label,
     352        db.branch_name, db.region, dtt.type_name,
     353        COUNT(*), ROUND(SUM(ft.amount_eur),2),
     354        SUM(ft.is_suspicious::INT), CURRENT_TIMESTAMP
     355    FROM fact_transaction ft
     356    JOIN dim_time dt ... JOIN dim_branch db ... JOIN dim_transaction_type dtt ...
     357    GROUP BY dt.year_num, dt.month_num, dt.quarter_label,
     358             db.branch_name, db.region, dtt.type_name;
     359
     360    -- 2. Квартални кредити
     361    TRUNCATE summary_quarterly_loans;
     362    INSERT INTO summary_quarterly_loans (...) SELECT ...;
     363
     364    -- 3. Квартални доцнења
     365    TRUNCATE summary_quarterly_debt;
     366    INSERT INTO summary_quarterly_debt (...) SELECT ...;
     367
     368    RAISE NOTICE 'Refreshed at %', CURRENT_TIMESTAMP;
     369END;
     370$$;
     371
     372-- Повик: CALL refresh_data_cube_summaries();
     373}}}
     374
     375== 8. Вредност и придобивки од решението ==
     376
     377=== 8.1 Поддршка при донесување одлуки ===
     378
     379Системот обезбедува брз и едноставен пристап до клучни деловни информации поврзани со трансакциската активност, кредитното портфолио, ризичните индикатори и доцнењата во отплатата на кредити. Наместо податоците да се собираат и обработуваат рачно, корисниците можат веднаш да добијат аналитички извештаи и показатели потребни за носење информирани одлуки.
     380
     381=== 8.2 Управување со ризик ===
     382
     383Преку анализата на NPL (Non-Performing Loans), LTV соодносот и детекцијата на сомнителни трансакции, системот овозможува рано идентификување на потенцијални проблеми. Анализите можат да се извршуваат на различни нивоа, вклучувајќи региони и филијали.
     384
     385=== 8.3 Оперативна ефикасност ===
     386
     387 * Автоматизирано освежување — без рачна интервенција
     388 * Partial индекси — помала меморија, побрзи пребарувања на сомнителни трансакции
     389 * Summary табели — извештаи за секунди наместо минути
     390
     391=== 8.4 Заклучок ===
     392
     393Во рамките на овој проект е дизајнирано и имплементирано Data Warehouse решение базирано на Star Schema архитектура и OLAP концепти. Системот користи димензионални и факт-табели за структурирање на податоците, додека аналитичките барања се реализирани преку CUBE, ROLLUP, GROUPING SETS и Window Functions.
     394
     395Преку summary табели, partial индекси и автоматска refresh процедура се обезбедуваат високи перформанси и точност на извештаите. Решението демонстрира практична примена на концептите од Data Warehousing и Business Intelligence во реален банкарски контекст, со цел изградба на скалабилна и аналитички ефикасна платформа.
     396
     397----
     398''Data Cube — BankPaymentService''