= Напредни извештаи од базата (PostgreSQL) = ---- Оваа страна прикажува 3 покомплексни извештаи. == Извршени се SQL надградбите (views, triggers, function, indexes) преку Node:== {{{ cd backend npm run db:apply }}} {{{ "Enhancements applied (views, triggers, function, indexes)." }}} Потоа внесовме мал демо-сет: {{{ npm run db:seed }}} {{{ Seed done. User IDs: { ana: '12', bojan: '13', ciro: '14', classId: '7' } }}} ---- == 1) Топ потрошувачи (пакети + мерч) со рангирање == **Цел:** По корисник, колку потрошил (сума на пакети + мерч) и кој му е рангот според вкупниот трошок. **Користен поглед:** `vw_user_spend` **SQL:** {{{ SELECT user_id, username, email, spend_packages, spend_merch, total_spend, RANK() OVER (ORDER BY total_spend DESC) AS spend_rank FROM vw_user_spend ORDER BY total_spend DESC, user_id; }}} **Релациска алгебра:** {{{ Π_{user_id,username,email,spend_packages,spend_merch,total_spend} ( User ⟕ (γ_{user_id; SUM(price)→spend_packages}(User_Purchased_Package ⋈ Package)) ⟕ (γ_{user_id; SUM(price)→spend_merch}(User_Purchased_Merch ⋈ Merch_Items)) ) }}} **Извршување:** {{{ npm run db:reports ги создава датотеките: backend/proofs/top_spenders.json backend/proofs/class_utilization.json backend/proofs/training_pop_monthly.json }}} {{{ [ { "user_id": "8", "username": "mikiYoga", "email": "miki@example.com", "spend_packages": "45.00", "spend_merch": "12.00", "total_spend": "57.00", "spend_rank": "1" },... ] }}} ---- == 2) Исполнетост на часови со дневно рангирање == **Цел:** За секој час: booked/capacity (во %) и ранг за истиот ден. **Користен поглед:** `vw_class_utilization` **SQL:** {{{ SELECT *, DENSE_RANK() OVER (PARTITION BY date ORDER BY utilization_pct DESC NULLS LAST) AS daily_rank FROM vw_class_utilization ORDER BY date, start_time, class_id; }}} **Релациска алгебра:** {{{ γ_{class_id; COUNT(user_id)→booked}(Class ⟕ User_Booked_Class) + калкулација utilization_pct = booked / capacity }}} {{{ [ { "class_id": "5", "date": "2025-06-09T22:00:00.000Z", "start_time": "08:00:00", "end_time": "09:00:00", "location": "Studio A", "capacity": 20, "booked": "1", "utilization_pct": "5.00", "instructor_id": "5", "daily_rank": "2" },... ] }}} ---- == 3) Популарност на тренинзи по месец == **Цел:** По месец, броиме уникатни корисници кои резервирале часови што содржат даден тренинг; даваме месечен ранг. **Користен поглед:** `vw_training_pop_monthly` **SQL:** {{{ SELECT training_id, training_name, month, num_bookings, RANK() OVER (PARTITION BY month ORDER BY num_bookings DESC NULLS LAST) AS rank_in_month FROM vw_training_pop_monthly ORDER BY month DESC, rank_in_month, training_name; }}} **Релациска алгебра:** {{{ γ_{training_id,month; COUNT(DISTINCT user_id)→num_bookings} ( Training ⋈ Class_Includes_Training ⋈ Class ⟕ User_Booked_Class ) }}} {{{ [ { "training_id": "7", "training_name": "Vinyasa", "month": "2025-08-31T22:00:00.000Z", "num_bookings": "1", "rank_in_month": "1" },... ] }}} ---- == 4) EXPLAIN/ANALYZE (доказ за индекси) == Ги генерираме плановите: {{{ npm run db:explain }}} *Напомена:* Поради мал сет, за демонстрација користевме и „forced“ планови со `enable_seqscan=off`. Фајлови: - `backend/proofs/events_explain_forced.json` - `backend/proofs/class_bookings_explain_forced.json` === A) Настани по датум/време – составен индекс (date,time) === Фајл: `backend/proofs/events_explain_forced.json` Очекуван извадок (Index Scan преку **idx_event_date_time**): {{{json { "Node Type": "Index Scan", "Index Name": "idx_event_date_time", "Relation Name": "Event", "Alias": "Event", "Index Cond": "(date >= CURRENT_DATE)" } }}} === B) Резервации по час – два индекса === Фајл: `backend/proofs/class_bookings_explain_forced.json` 1) Индекс врз Class за датум: {{{json { "Node Type": "Index Scan", "Index Name": "idx_class_date_time", "Relation Name": "Class", "Alias": "c", "Index Cond": "(date >= CURRENT_DATE)" } }}} 2) Индекс врз User_Booked_Class за поврзување по class_id: {{{json { "Node Type": "Index Scan", "Index Name": "idx_ubc_class", "Relation Name": "User_Booked_Class", "Alias": "ubc", "Index Cond": "(class_id = c.class_id)" } }}}