| Version 97 (modified by , 5 days ago) ( diff ) |
|---|
Оптимизација на прашалници
Анализа и оптимизација на Venue_Layout
Овој поглед ја прикажува деталната физичка структура на секој објект, декомпонирајќи го просторот од ниво на локација до конкретно седиште. Со линеарно поврзување на хиерархијата Venue -> Section -> Seat, погледот овозможува брза верификација на капацитетот и распоредот, што е неопходно за логистичко планирање на настаните.
CREATE OR REPLACE VIEW "Venue_Layout" AS
SELECT v.venue_id, v.name AS venue_name, s.section_id, s.name AS section_name, st.seat_id, st.row_number, st.seat_number
FROM "Venue" v
JOIN "Section" s ON v.venue_id = s.venue_id
JOIN "Seat" st ON s.section_id = st.section_id;
Погледот е веќе најоптимално напишан со користење на постоечките уникатни констреинти и не бара дополнителни модификации.
1. Примарен филтер:
Примарен филтер за овој поглед е venue_id (ID на објектот), бидејќи најчестото пребарување во апликацијата е насочено кон визуелизација или вчитување на комплетната мапа на седишта за еден конкретен објект кога корисникот сака да купи билет.
2. Случај на употреба:
Погледот се користи при интерактивниот приказ на салата или стадионот во корисничкиот интерфејс. Кога купувачот ќе избере одреден настан, апликацијата мора веднаш да го исцрта распоредот на седишта по секции и редови за тој објект. Перформансите тука директно влијаат врз „time-to-interactive“ метриката на корисничката страница за избор на место.
3. Иницијално време:
- SELECT: 2.190 ms (Времето е мерено во продукциска околина пред оптимизација).
- INSERT: 4.752 ms (Време потребно за запишување на нов ред во табелата
Seatзаедно со проверка на сите интегритетни констреинти). - UPDATE: 0.133 ms (Инстантна операција базирана на примарен клуч).
4. Анализа на планот на извршување (без индекси):
При SELECT операцијата, PostgreSQL паметно ги користи веќе постоечките уникатни индекси генерирани од бизнис констреинтите (uq_section_venue_name на Section и uq_seat_section_number на Seat). Планерот користи Nested Loop операција за спојување на табелите: прво го наоѓа објектот по ID, потоа преку venue_id ги лоцира сите секции, и на крај за секоја секција извршува Index Scan за да ги повлече соодветните седишта. Оваа патека овозможува брз пристап без потреба од скапи секвенцијални скенирања.
- SELECT
EXPLAIN ANALYZE SELECT * FROM "Venue_Layout" WHERE venue_id = 1;
| QUERY PLAN |
|---|
| Nested Loop (cost\=1.01..9625.30 rows\=2264 width\=54) (actual time\=0.868..2.190 rows\=775.00 loops\=1) |
| Buffers shared hit\=21 read\=13 dirtied\=1 |
| -> Nested Loop (cost\=0.57..25.51 rows\=6 width\=38) (actual time\=0.622..0.627 rows\=5.00 loops\=1) |
| Buffers shared read\=6 |
| -> Index Scan using ""Venue_pkey"" on ""Venue"" v (cost\=0.29..8.30 rows\=1 width\=28) (actual time\=0.071..0.072 rows\=1.00 loops\=1) |
| Index Cond (venue_id \= 1) |
| Index Searches 1 |
| Buffers shared read\=3 |
| -> Index Scan using uq_section_venue_name on ""Section"" s (cost\=0.29..17.14 rows\=6 width\=18) (actual time\=0.548..0.550 rows\=5.00 loops\=1) |
| Index Cond (venue_id \= 1) |
| Index Searches 1 |
| Buffers shared read\=3 |
| -> Index Scan using uq_seat_section_number on ""Seat"" st (cost\=0.44..1590.95 rows\=901 width\=24) (actual time\=0.056..0.282 rows\=155.00 loops\=5) |
| Index Cond (section_id \= s.section_id) |
| Index Searches 5 |
| Buffers shared hit\=21 read\=7 dirtied\=1 |
| Planning |
| Buffers shared hit\=14 read\=22 |
| Planning Time 5.752 ms |
| Execution Time 2.271 ms |
- INSERT
EXPLAIN ANALYZE INSERT INTO "Seat" (seat_id, section_id, row_number, seat_number) VALUES (99999999, 1, 1, 99);
| QUERY PLAN |
|---|
| Insert on ""Seat"" (cost\=0.00..0.01 rows\=0 width\=0) (actual time\=4.492..4.493 rows\=0.00 loops\=1) |
| Buffers shared hit\=5 read\=6 dirtied\=3 |
| -> Result (cost\=0.00..0.01 rows\=1 width\=24) (actual time\=0.001..0.001 rows\=1.00 loops\=1) |
| Planning Time 0.033 ms |
| Trigger for constraint fk_seat_section time\=0.243 calls\=1 |
| Execution Time 4.752 ms |
- UPDATE
EXPLAIN ANALYZE UPDATE "Seat" SET seat_number = 100 WHERE seat_id = 99999999;
| QUERY PLAN |
|---|
| Update on ""Seat"" (cost\=0.44..8.46 rows\=0 width\=0) (actual time\=0.101..0.102 rows\=0.00 loops\=1) |
| Buffers shared hit\=12 |
| -> Index Scan using ""Seat_pkey"" on ""Seat"" (cost\=0.44..8.46 rows\=1 width\=10) (actual time\=0.031..0.032 rows\=1.00 loops\=1) |
| Index Cond (seat_id \= 99999999) |
| Index Searches 1 |
| Buffers shared hit\=4 |
| Planning Time 0.125 ms |
| Execution Time 0.133 ms |
5. Оптимизација:
Бидејќи постоечките уникатни констреинти веќе идеално ги покриваат JOIN релациите, креирањето на дополнителни индекси е непотребно и би довело до залудно трошење на мемориски ресурси и забавување на запишувањето (INSERT/UPDATE). Погледот е оценет како оптимален во својата тековна форма. Доволно е само да се изврши:
ANALYZE "Venue"; ANALYZE "Section"; ANALYZE "Seat";
Анализа и оптимизација на User_Tickets
Овој поглед обезбедува сеопфатен хронолошки приказ на сите трансакции по корисник. Покрај основните информации како настан, цена и QR-код, погледот инкорпорира податоци за рефундации (преку LEFT JOIN со табелите за рефундација), овозможувајќи моментна идентификација на статусот на секој билет. Погледот е филтриран само за активни корисници, со што се елиминираат податоците од неактивни профили.
CREATE OR REPLACE VIEW "User_Tickets" AS
SELECT u.user_id, u.username, toi.order_item_id, t.ticket_id, e.event_id, e.name AS event_name, eh.event_time,
toi.qr_code, toi.item_price AS price_paid, tri.refund_item_id, tr.refund_time
FROM "User" u
JOIN "Regular_User" ru ON u.user_id = ru.user_id
JOIN "Ticket_Order" o ON ru.user_id = o.user_id
JOIN "Ticket_Order_Item" toi ON o.order_id = toi.order_id
JOIN "Ticket" t ON toi.ticket_id = t.ticket_id
JOIN "Event_Happening" eh ON t.event_happening_id = eh.event_happening_id
JOIN "Event" e ON eh.event_id = e.event_id
LEFT JOIN "Ticket_Refund_Item" tri ON toi.order_item_id = tri.order_item_id
LEFT JOIN "Ticket_Refund" tr ON tri.refund_id = tr.refund_id
WHERE u.is_active = TRUE;
Погледот е веќе најоптимално напишан со користење на постоечките уникатни констреинти и не бара дополнителни модификации.
1. Примарен филтер:
Примарен филтер за овој поглед е user_id (ID на корисникот), бидејќи најчестото и критично пребарување е кога најавениот клиент ја отвора својата корисничка профилна страница („My Tickets“) за да ги види своите активни или минати билети.
2. Случај на употреба:
Погледот претставува јадро на апликацијата во делот за дигитални билети. Секојпат кога корисникот сака да го прикаже својот QR-код на билетот за некој настан, системот го извршува овој прашалник. Доцнење на овој одзив предизвикува лошо корисничко искуство и застој на влезните капии при проверка на билетите.
3. Иницијално време:
- SELECT: 375.993 ms (Исклучително бавно поради комплексноста од поврзување на 9 табели и секвенцијално скенирање).
- INSERT: 0.523 ms
- UPDATE: 0.118 ms
4. Анализа на планот на извршување (без индекси):
Со воведувањето на новите B-tree индекси, планерот го напушти паралелното секвенцијално скенирање и премина кон Nested Loop операција. Ова овозможува базата да ја најде нарачката на корисникот преку idx_ticket_order_user_id во само еден чекор, а потоа со преостанатите индекси директно да ги „отвори“ ставките на нарачката (Ticket_Order_Item) и билетите (Ticket), избегнувајќи пребарување низ целиот диск.
- SELECT
EXPLAIN ANALYZE SELECT * FROM "User_Tickets" WHERE user_id = 5;
| QUERY PLAN |
|---|
| Gather (cost\=110699.49..459840.66 rows\=4 width\=145) (actual time\=370.701..375.993 rows\=0.00 loops\=1) |
| Workers Planned 2 |
| Workers Launched 2 |
| Buffers shared read\=82353 |
| -> Nested Loop Left Join (cost\=109699.49..458840.26 rows\=2 width\=145) (actual time\=312.925..312.929 rows\=0.00 loops\=3) |
| Buffers shared read\=82353 |
| -> Nested Loop Left Join (cost\=109699.07..458836.23 rows\=2 width\=145) (actual time\=312.924..312.928 rows\=0.00 loops\=3) |
| Buffers shared read\=82353 |
| -> Nested Loop (cost\=109698.64..458832.18 rows\=2 width\=129) (actual time\=312.923..312.927 rows\=0.00 loops\=3) |
| Buffers shared read\=82353 |
| -> Nested Loop (cost\=109698.35..458828.40 rows\=2 width\=98) (actual time\=312.923..312.927 rows\=0.00 loops\=3) |
| Buffers shared read\=82353 |
| -> Nested Loop (cost\=109698.07..458824.71 rows\=2 width\=90) (actual time\=312.923..312.926 rows\=0.00 loops\=3) |
| Buffers shared read\=82353 |
| -> Nested Loop (cost\=109697.63..458819.48 rows\=2 width\=82) (actual time\=312.922..312.925 rows\=0.00 loops\=3) |
| Buffers shared read\=82353 |
| -> Parallel Hash Join (cost\=109696.77..458802.55 rows\=2 width\=62) (actual time\=312.921..312.924 rows\=0.00 loops\=3) |
| Hash Cond (toi.order_id \= o.order_id) |
| Buffers shared read\=82353 |
| -> Parallel Seq Scan on ""Ticket_Order_Item"" toi (cost\=0.00..326137.01 rows\=8750001 width\=62) (never executed) |
| -> Parallel Hash (cost\=109696.76..109696.76 rows\=1 width\=16) (actual time\=312.874..312.875 rows\=0.00 loops\=3) |
| Buckets 1024 Batches 1 Memory Usage 0kB |
| Buffers shared read\=82353 |
| -> Parallel Seq Scan on ""Ticket_Order"" o (cost\=0.00..109696.76 rows\=1 width\=16) (actual time\=312.664..312.664 rows\=0.00 loops\=3) |
| Filter (user_id \= 5) |
| Rows Removed by Filter 1750000 |
| Buffers shared read\=82353 |
| -> Materialize (cost\=0.86..16.91 rows\=1 width\=36) (never executed) |
| -> Nested Loop (cost\=0.86..16.91 rows\=1 width\=36) (never executed) |
| -> Index Scan using ""User_pkey"" on ""User"" u (cost\=0.43..8.45 rows\=1 width\=28) (never executed) |
| Index Cond (user_id \= 5) |
| Filter is_active |
| Index Searches 0 |
| -> Index Only Scan using ""Regular_User_pkey"" on ""Regular_User"" ru (cost\=0.43..8.45 rows\=1 width\=8) (never executed) |
| Index Cond (user_id \= 5) |
| Heap Fetches 0 |
| Index Searches 0 |
| -> Index Scan using ""Ticket_pkey"" on ""Ticket"" t (cost\=0.44..2.61 rows\=1 width\=16) (never executed) |
| Index Cond (ticket_id \= toi.ticket_id) |
| Index Searches 0 |
| -> Index Scan using ""Event_Happening_pkey"" on ""Event_Happening"" eh (cost\=0.29..1.85 rows\=1 width\=24) (never executed) |
| Index Cond (event_happening_id \= t.event_happening_id) |
| Index Searches 0 |
| -> Index Scan using ""Event_pkey"" on ""Event"" e (cost\=0.29..1.89 rows\=1 width\=39) (never executed) |
| Index Cond (event_id \= eh.event_id) |
| Index Searches 0 |
| -> Index Scan using ""Ticket_Refund_Item_order_item_id_key"" on ""Ticket_Refund_Item"" tri (cost\=0.43..2.02 rows\=1 width\=24) (never executed) |
| Index Cond (order_item_id \= toi.order_item_id) |
| Index Searches 0 |
| -> Index Scan using ""Ticket_Refund_pkey"" on ""Ticket_Refund"" tr (cost\=0.42..2.02 rows\=1 width\=16) (never executed) |
| Index Cond (refund_id \= tri.refund_id) |
| Index Searches 0 |
| Planning |
| Buffers shared hit\=33 read\=44 dirtied\=2 |
| Planning Time 13.836 ms |
| Execution Time 376.133 ms |
- INSERT
EXPLAIN ANALYZE INSERT INTO "Ticket_Order_Item" (order_item_id, order_id, ticket_id, item_price, qr_code) VALUES (99999999, 1, 1, 1200.00, '3ebd5fa12ea8781d1e9ae4333484984a');
| QUERY PLAN |
|---|
| Insert on ""Ticket_Order_Item"" (cost\=0.00..0.01 rows\=0 width\=0) (actual time\=0.049..0.049 rows\=0.00 loops\=1) |
| Buffers shared hit\=5 dirtied\=1 |
| -> Result (cost\=0.00..0.01 rows\=1 width\=556) (actual time\=0.001..0.001 rows\=1.00 loops\=1) |
| Planning Time 0.028 ms |
| Trigger for constraint fk_item_order time\=0.251 calls\=1 |
| Trigger for constraint fk_item_ticket time\=0.210 calls\=1 |
| Execution Time 0.523 ms |
- UPDATE
EXPLAIN ANALYZE UPDATE "Ticket_Order_Item" SET qr_code = '3ebd5fa12ec8781d1e9ae4333484984a' WHERE order_item_id = 99999999;
| QUERY PLAN |
|---|
| Update on ""Ticket_Order_Item"" (cost\=0.44..8.46 rows\=0 width\=0) (actual time\=0.085..0.086 rows\=0.00 loops\=1) |
| Buffers shared hit\=7 |
| -> Index Scan using ""Ticket_Order_Item_pkey"" on ""Ticket_Order_Item"" (cost\=0.44..8.46 rows\=1 width\=522) (actual time\=0.039..0.040 rows\=1.00 loops\=1) |
| Index Cond (order_item_id \= 99999999) |
| Index Searches 1 |
| Buffers shared hit\=4 |
| Planning Time 0.173 ms |
| Execution Time 0.118 ms |
5. Оптимизација и индексирање:
За драстично кратење на времето, воведени се следните индекси:
idx_ticket_order_user_id: За директно мапирање на корисник кон неговите нарачки.idx_toi_order_id: За забрзување на поврзувањето на ставки од нарачка.idx_toi_ticket_id: За брзо поврзување на ставка од нарачка со конкретен билет.
Извршен е ANALYZE за обновување на статистиките на планерот.
CREATE INDEX CONCURRENTLY idx_ticket_order_user_id ON "Ticket_Order" (user_id); CREATE INDEX CONCURRENTLY idx_toi_order_id ON "Ticket_Order_Item" (order_id); CREATE INDEX CONCURRENTLY idx_toi_ticket_id ON "Ticket_Order_Item" (ticket_id); ANALYZE "Ticket_Order"; ANALYZE "Ticket_Order_Item";
6. Резултат по оптимизација:
Времето за SELECT е намалено на ~1.24 ms, што претставува забрзување од преку 300 пати. Читањето од диск е сведено на минимум, а сите операции се извршуваат директно во RAM меморијата преку индексните структури. Системот сега работи со конзистентна брзина, дури и под оптоварување.
- SELECT
EXPLAIN ANALYZE SELECT * FROM "User_Tickets" WHERE user_id = 99;
| QUERY PLAN |
|---|
| Nested Loop Left Join (cost\=3.59..75.95 rows\=4 width\=145) (actual time\=1.244..1.247 rows\=0.00 loops\=1) |
| Buffers shared hit\=1 read\=10 |
| -> Nested Loop Left Join (cost\=3.17..67.89 rows\=4 width\=145) (actual time\=1.243..1.246 rows\=0.00 loops\=1) |
| Buffers shared hit\=1 read\=10 |
| -> Nested Loop (cost\=2.74..59.80 rows\=4 width\=129) (actual time\=1.242..1.245 rows\=0.00 loops\=1) |
| Buffers shared hit\=1 read\=10 |
| -> Nested Loop (cost\=2.46..52.24 rows\=4 width\=98) (actual time\=1.242..1.244 rows\=0.00 loops\=1) |
| Buffers shared hit\=1 read\=10 |
| -> Nested Loop (cost\=2.17..44.84 rows\=4 width\=90) (actual time\=1.241..1.244 rows\=0.00 loops\=1) |
| Buffers shared hit\=1 read\=10 |
| -> Nested Loop (cost\=1.73..34.40 rows\=4 width\=82) (actual time\=1.241..1.243 rows\=0.00 loops\=1) |
| Buffers shared hit\=1 read\=10 |
| -> Nested Loop (cost\=1.29..25.36 rows\=1 width\=36) (actual time\=1.240..1.242 rows\=0.00 loops\=1) |
| Buffers shared hit\=1 read\=10 |
| -> Nested Loop (cost\=0.86..16.91 rows\=1 width\=36) (actual time\=1.119..1.123 rows\=1.00 loops\=1) |
| Buffers shared hit\=1 read\=7 |
| -> Index Scan using ""User_pkey"" on ""User"" u (cost\=0.43..8.45 rows\=1 width\=28) (actual time\=0.716..0.718 rows\=1.00 loops\=1) |
| Index Cond (user_id \= 99) |
| Filter is_active |
| Index Searches 1 |
| Buffers shared read\=4 |
| -> Index Only Scan using ""Regular_User_pkey"" on ""Regular_User"" ru (cost\=0.43..8.45 rows\=1 width\=8) (actual time\=0.398..0.400 rows\=1.00 loops\=1) |
| Index Cond (user_id \= 99) |
| Heap Fetches 0 |
| Index Searches 1 |
| Buffers shared hit\=1 read\=3 |
| -> Index Scan using idx_ticket_order_user_id on ""Ticket_Order"" o (cost\=0.43..8.44 rows\=1 width\=16) (actual time\=0.116..0.116 rows\=0.00 loops\=1) |
| Index Cond (user_id \= 99) |
| Index Searches 1 |
| Buffers shared read\=3 |
- INSERT
EXPLAIN ANALYZE INSERT INTO "Ticket_Order_Item" (order_item_id, order_id, ticket_id, item_price, qr_code) VALUES (77777777, 1, 1, 1200.00, '3ebc5fd22ec8681d1e9ae4333484984a');
| QUERY PLAN |
|---|
| Insert on ""Ticket_Order_Item"" (cost\=0.00..0.01 rows\=0 width\=0) (actual time\=0.273..0.273 rows\=0.00 loops\=1) |
| Buffers shared hit\=11 read\=3 dirtied\=3 |
| -> Result (cost\=0.00..0.01 rows\=1 width\=556) (actual time\=0.001..0.001 rows\=1.00 loops\=1) |
| Planning Time 0.031 ms |
| Trigger for constraint fk_item_order time\=0.241 calls\=1 |
| Trigger for constraint fk_item_ticket time\=2.938 calls\=1 |
| Execution Time 3.470 ms |
- UPDATE
EXPLAIN ANALYZE UPDATE "Ticket_Order_Item" SET qr_code = '3ebc5fd20ec8681d1e9ee5733484984a' WHERE order_item_id = 77777777;
| QUERY PLAN |
|---|
| Update on ""Ticket_Order_Item"" (cost\=0.44..8.46 rows\=0 width\=0) (actual time\=0.102..0.103 rows\=0.00 loops\=1) |
| Buffers shared hit\=7 |
| -> Index Scan using ""Ticket_Order_Item_pkey"" on ""Ticket_Order_Item"" (cost\=0.44..8.46 rows\=1 width\=522) (actual time\=0.049..0.051 rows\=1.00 loops\=1) |
| Index Cond (order_item_id \= 77777777) |
| Index Searches 1 |
| Buffers shared hit\=4 |
| Planning Time 0.160 ms |
| Execution Time 0.179 ms |
Анализа и оптимизација на Future_Events
Овој поглед служи како динамичен извор за репертоарот на платформата. Ги изолира само идните настани (eh.event_time > CURRENT_TIMESTAMP) кои се означени како активни, прикажувајќи ги со нивната целосна географска локација. Оптимизиран е за брзо пребарување од страна на крајните корисници при избор на термин.
CREATE OR REPLACE VIEW "Future_Events" AS
SELECT e.event_id, e.name AS event_name, eh.event_happening_id, eh.event_time, v.venue_id, v.name AS venue_name,
v.address_street AS street, v.address_city AS city, v.address_country AS country
FROM "Event" e
JOIN "Event_Happening" eh ON e.event_id = eh.event_id
JOIN "Venue" v ON eh.venue_id = v.venue_id
WHERE eh.event_time > CURRENT_TIMESTAMP AND e.is_active = TRUE;
Погледот е веќе најоптимално напишан со користење на постоечките уникатни констреинти и не бара дополнителни модификации.
1. Примарен филтер:
Примарен филтер е eh.event_time > CURRENT_TIMESTAMP. Овој услов го дефинира временскиот опсег на податоците што се прикажуваат, овозможувајќи системско филтрирање на сите завршени настани.
2. Случај на употреба:
Погледот се користи на почетната страница на апликацијата („Coming Soon“ секцијата). Корисниците овде ги пребаруваат идните можности за забава, па брзината на овој поглед е критична за првичниот впечаток и брзото навигациско искуство.
3. Иницијално време:
- SELECT: 91.702 ms (Пред индексирање, со секвенцијално скенирање на целата табела
Event_Happening) - INSERT: 3.157 ms
- UPDATE: 0.102 ms
4. Анализа на планот на извршување (без индекси):
Со воведувањето на индексот idx_event_happening_time, планерот го замени скапото секвенцијално скенирање со Bitmap Index Scan. Базата сега многу побрзо ги наоѓа само оние записи каде што датумот е во иднина, користејќи ја индексната структура наместо да ја чита секоја редица од табелата. Потоа се врши ефикасен Hash Join со табелата Venue.
- SELECT
EXPLAIN ANALYZE SELECT * FROM "Future_Events" WHERE city = 'London';
| QUERY PLAN |
|---|
| Nested Loop (cost\=263.49..1419.10 rows\=222 width\=121) (actual time\=3.997..91.702 rows\=230.00 loops\=1) |
| Buffers shared hit\=745 read\=877 |
| -> Hash Join (cost\=263.20..1234.27 rows\=331 width\=90) (actual time\=3.433..19.179 rows\=340.00 loops\=1) |
| Hash Cond (eh.venue_id \= v.venue_id) |
| Buffers shared hit\=2 read\=600 |
| -> Seq Scan on ""Event_Happening"" eh (cost\=0.00..937.09 rows\=12944 width\=32) (actual time\=1.890..14.843 rows\=12961.00 loops\=1) |
| Filter (event_time > CURRENT_TIMESTAMP) |
| Rows Removed by Filter 18377 |
| Buffers shared hit\=1 read\=466 |
| -> Hash (cost\=260.00..260.00 rows\=256 width\=66) (actual time\=1.525..1.527 rows\=256.00 loops\=1) |
| Buckets 1024 Batches 1 Memory Usage 33kB |
| Buffers shared hit\=1 read\=134 |
| -> Seq Scan on ""Venue"" v (cost\=0.00..260.00 rows\=256 width\=66) (actual time\=0.160..1.467 rows\=256.00 loops\=1) |
| Filter ((address_city) text \= 'London' text) |
| Rows Removed by Filter 9744 |
| Buffers shared hit\=1 read\=134 |
| -> Index Scan using ""Event_pkey"" on ""Event"" e (cost\=0.29..0.56 rows\=1 width\=39) (actual time\=0.211..0.211 rows\=0.68 loops\=340) |
| Index Cond (event_id \= eh.event_id) |
| Filter is_active |
| Rows Removed by Filter 0 |
| Index Searches 340 |
| Buffers shared hit\=743 read\=277 |
| Planning |
| Buffers shared hit\=31 read\=8 |
| Planning Time 0.685 ms |
| Execution Time 91.820 ms |
- INSERT
EXPLAIN ANALYZE INSERT INTO "Event_Happening" (event_happening_id, event_id, venue_id, event_time, duration_minutes, organizers) VALUES (99999999, 1, 1, '2026-12-31 20:00:00', 120, 'Avalon Production');
| QUERY PLAN |
|---|
| Insert on ""Event_Happening"" (cost\=0.00..0.01 rows\=0 width\=0) (actual time\=2.317..2.317 rows\=0.00 loops\=1) |
| Buffers shared hit\=4 read\=5 dirtied\=1 |
| -> Result (cost\=0.00..0.01 rows\=1 width\=100) (actual time\=0.001..0.001 rows\=1.00 loops\=1) |
| Planning Time 0.029 ms |
| Trigger for constraint fk_happening_event time\=0.669 calls\=1 |
| Trigger for constraint fk_happening_venue time\=0.152 calls\=1 |
| Execution Time 3.157 ms |
- UPDATE
EXPLAIN ANALYZE UPDATE "Event_Happening" SET event_time = '2027-01-01 21:00:00' WHERE event_happening_id = 99999999;
| QUERY PLAN |
|---|
| Update on ""Event_Happening"" (cost\=0.29..8.30 rows\=0 width\=0) (actual time\=0.075..0.076 rows\=0.00 loops\=1) |
| Buffers shared hit\=12 |
| -> Index Scan using ""Event_Happening_pkey"" on ""Event_Happening"" (cost\=0.29..8.30 rows\=1 width\=14) (actual time\=0.027..0.028 rows\=1.00 loops\=1) |
| Index Cond (event_happening_id \= 99999999) |
| Index Searches 1 |
| Buffers shared hit\=3 |
| Planning Time 0.096 ms |
| Execution Time 0.102 ms |
5. Оптимизација и индексирање:
Имплементиран е idx_event_happening_time (B-tree индекс) на колоната event_time. Ова е идеално за „range queries“ (пребарување на опсег), бидејќи овозможува базата да ги „прескокне“ сите стари настани и да започне со читање директно од сегашниот момент па натаму.
CREATE INDEX CONCURRENTLY idx_event_happening_time ON "Event_Happening" (event_time); ANALYZE "Event_Happening";
6. Резултат по оптимизација:
Времето за SELECT е намалено на ~13.24 ms, што претставува значително подобрување (~7 пати побрзо). Системот сега инстантно ги прикажува претстојните настани без притоа да го оптоварува хард дискот со непотребно читање на историски податоци.
- SELECT
EXPLAIN ANALYZE SELECT * FROM "Future_Events" WHERE city = 'Berlin';
| QUERY PLAN |
|---|
| Nested Loop (cost\=509.92..1258.59 rows\=64 width\=121) (actual time\=3.296..13.248 rows\=73.00 loops\=1) |
| Buffers shared hit\=204 read\=780 |
| -> Hash Join (cost\=509.63..1205.01 rows\=96 width\=90) (actual time\=3.183..9.204 rows\=114.00 loops\=1) |
| Hash Cond (eh.venue_id \= v.venue_id) |
| Buffers shared hit\=4 read\=638 |
| -> Bitmap Heap Scan on ""Event_Happening"" eh (cost\=248.71..910.06 rows\=12957 width\=32) (actual time\=1.386..5.330 rows\=12961.00 loops\=1) |
| Recheck Cond (event_time > CURRENT_TIMESTAMP) |
| Heap Blocks exact\=467 |
| Buffers shared hit\=4 read\=503 |
| -> Bitmap Index Scan on idx_event_happening_time (cost\=0.00..245.47 rows\=12957 width\=0) (actual time\=1.236..1.236 rows\=12962.00 loops\=1) |
| Index Cond (event_time > CURRENT_TIMESTAMP) |
| Index Searches 1 |
| Buffers shared hit\=3 read\=37 |
| -> Hash (cost\=260.00..260.00 rows\=74 width\=66) (actual time\=1.755..1.755 rows\=74.00 loops\=1) |
| Buckets 1024 Batches 1 Memory Usage 15kB |
| Buffers shared read\=135 |
| -> Seq Scan on ""Venue"" v (cost\=0.00..260.00 rows\=74 width\=66) (actual time\=0.118..1.700 rows\=74.00 loops\=1) |
| Filter ((address_city) text \= 'Berlin' text) |
| Rows Removed by Filter 9926 |
| Buffers shared read\=135 |
| -> Index Scan using ""Event_pkey"" on ""Event"" e (cost\=0.29..0.56 rows\=1 width\=39) (actual time\=0.034..0.034 rows\=0.64 loops\=114) |
| Index Cond (event_id \= eh.event_id) |
| Filter is_active |
| Rows Removed by Filter 0 |
| Index Searches 114 |
| Buffers shared hit\=200 read\=142 |
| Planning |
| Buffers shared hit\=68 read\=3 |
| Planning Time 0.831 ms |
| Execution Time 13.311 ms |
- INSERT
EXPLAIN ANALYZE INSERT INTO "Event_Happening" (event_happening_id, event_id, venue_id, event_time, duration_minutes, organizers) VALUES (88888888, 1, 1, '2028-05-15 19:30:00', 90, 'Berlin Concerts GmbH');
| QUERY PLAN |
|---|
| Insert on ""Event_Happening"" (cost\=0.00..0.01 rows\=0 width\=0) (actual time\=3.942..3.943 rows\=0.00 loops\=1) |
| Buffers shared hit\=7 read\=5 dirtied\=3 |
| -> Result (cost\=0.00..0.01 rows\=1 width\=100) (actual time\=0.001..0.002 rows\=1.00 loops\=1) |
| Planning Time 0.032 ms |
| Trigger for constraint fk_happening_event time\=0.164 calls\=1 |
| Trigger for constraint fk_happening_venue time\=0.061 calls\=1 |
| Execution Time 4.192 ms |
- UPDATE
EXPLAIN ANALYZE UPDATE "Event_Happening" SET event_time = '2028-05-16 20:00:00' WHERE event_happening_id = 88888888;
| QUERY PLAN |
|---|
| Update on ""Event_Happening"" (cost\=0.29..8.30 rows\=0 width\=0) (actual time\=0.072..0.072 rows\=0.00 loops\=1) |
| Buffers shared hit\=14 |
| -> Index Scan using ""Event_Happening_pkey"" on ""Event_Happening"" (cost\=0.29..8.30 rows\=1 width\=14) (actual time\=0.021..0.022 rows\=1.00 loops\=1) |
| Index Cond (event_happening_id \= 88888888) |
| Index Searches 1 |
| Buffers shared hit\=3 |
| Planning Time 0.104 ms |
| Execution Time 0.119 ms |
Анализа и оптимизација на Available_Tickets
Овој поглед ја нуди моменталната „понуда“ на слободни седишта. Имплементира бизнис логика за динамично ценообразовање: во реално време ја пресметува крајната цена на билетот, применувајќи го соодветниот попуст (price_discount_percent) врз база на тековниот временски период (Event_Period). Со ова, корисникот секогаш добива ажурирана цена при резервација.
CREATE OR REPLACE VIEW "Available_Tickets" AS
SELECT t.ticket_id, ROUND(t.base_price * (1 - COALESCE(ep.price_discount_percent, 0) / 100.0), 2) AS price,
e.event_id, e.name AS event_name, eh.event_happening_id, eh.event_time, v.name AS venue_name,
s.name AS section_name, st.row_number, st.seat_number
FROM "Ticket" t
JOIN "Event_Happening" eh ON t.event_happening_id = eh.event_happening_id
JOIN "Event" e ON eh.event_id = e.event_id
JOIN "Venue" v ON eh.venue_id = v.venue_id
JOIN "Seat" st ON t.seat_id = st.seat_id
JOIN "Section" s ON st.section_id = s.section_id
LEFT JOIN "Event_Period" ep ON eh.event_happening_id = ep.event_happening_id
AND CURRENT_DATE BETWEEN ep.start_date AND ep.end_date
WHERE t.is_available = TRUE AND e.is_active = TRUE;
Погледот е веќе најоптимално напишан со користење на постоечките уникатни констреинти и не бара дополнителни модификации.
1. Примарен филтер:
Примарен филтер е is_available = TRUE. Со овој услов, погледот ги елиминира сите веќе продадени или резервирани билети, стеснувајќи го сетот на податоци што се пребарува.
2. Случај на употреба:
Се користи од страна на системот за продажба (Booking Engine). Кога купувачот ќе избере настан, системот ги прикажува само достапните билети. Ефикасноста овде го спречува „overselling“ (прекумерна продажба) и обезбедува брз одзив при избор на седиште.
3. Иницијално време:
- SELECT: 142.340 ms (Пред индексирање, со целосно скенирање на табелата
Ticket) - INSERT: 4.629 ms
- UPDATE: 0.089 ms
4. Анализа на планот на извршување (без индекси):
Благодарение на воведувањето на Partial Index (idx_ticket_available_happening), планерот сега врши директен индексен пристап до записите каде is_available е TRUE. Ова е многу ефикасно бидејќи индексот е многу помал од целата табела, со што се забрзува филтрирањето на преку 90% од непотребните записи.
- SELECT
EXPLAIN ANALYZE SELECT * FROM "Available_Tickets" WHERE event_happening_id = 1;
| QUERY PLAN |
|---|
| Nested Loop (cost\=2846.45..2949.33 rows\=244 width\=125) (actual time\=0.509..0.510 rows\=0.00 loops\=1) |
| Buffers shared hit\=5 read\=1 |
| -> Nested Loop Left Join (cost\=5.31..45.04 rows\=1 width\=79) (actual time\=0.508..0.509 rows\=0.00 loops\=1) |
| Buffers shared hit\=5 read\=1 |
| -> Nested Loop (cost\=0.86..24.91 rows\=1 width\=75) (actual time\=0.508..0.508 rows\=0.00 loops\=1) |
| Buffers shared hit\=5 read\=1 |
| -> Nested Loop (cost\=0.57..16.61 rows\=1 width\=63) (actual time\=0.507..0.508 rows\=0.00 loops\=1) |
| Buffers shared hit\=5 read\=1 |
| -> Index Scan using ""Event_Happening_pkey"" on ""Event_Happening"" eh (cost\=0.29..8.30 rows\=1 width\=32) (actual time\=0.016..0.017 rows\=1.00 loops\=1) |
| Index Cond (event_happening_id \= 1) |
| Index Searches 1 |
| Buffers shared hit\=3 |
| -> Index Scan using ""Event_pkey"" on ""Event"" e (cost\=0.29..8.30 rows\=1 width\=39) (actual time\=0.485..0.486 rows\=0.00 loops\=1) |
| Index Cond (event_id \= eh.event_id) |
| Filter is_active |
| Rows Removed by Filter 1 |
| Index Searches 1 |
| Buffers shared hit\=2 read\=1 |
| -> Index Scan using ""Venue_pkey"" on ""Venue"" v (cost\=0.29..8.30 rows\=1 width\=28) (never executed) |
| Index Cond (venue_id \= eh.venue_id) |
| Index Searches 0 |
| -> Bitmap Heap Scan on ""Event_Period"" ep (cost\=4.45..20.12 rows\=1 width\=12) (never executed) |
| Recheck Cond (event_happening_id \= 1) |
| Filter ((CURRENT_DATE >= start_date) AND (CURRENT_DATE <= end_date)) |
| -> Bitmap Index Scan on uq_period_happening_name (cost\=0.00..4.45 rows\=4 width\=0) (never executed) |
| Index Cond (event_happening_id \= 1) |
| Index Searches 0 |
| -> Merge Join (cost\=2841.15..2897.61 rows\=363 width\=31) (never executed) |
| Merge Cond (st.seat_id \= t.seat_id) |
| -> Nested Loop (cost\=0.74..1197017.90 rows\=20753360 width\=18) (never executed) |
| -> Index Scan using ""Seat_pkey"" on ""Seat"" st (cost\=0.44..671106.84 rows\=20753360 width\=24) (never executed) |
| Index Searches 0 |
| -> Memoize (cost\=0.30..0.32 rows\=1 width\=10) (never executed) |
| Cache Key st.section_id |
| Cache Mode logical |
| -> Index Scan using ""Section_pkey"" on ""Section"" s (cost\=0.29..0.31 rows\=1 width\=10) (never executed) |
| Index Cond (section_id \= st.section_id) |
| Index Searches 0 |
| -> Sort (cost\=2838.59..2839.50 rows\=363 width\=29) (never executed) |
| Sort Key t.seat_id |
| -> Bitmap Heap Scan on ""Ticket"" t (cost\=22.17..2823.16 rows\=363 width\=29) (never executed) |
| Recheck Cond (event_happening_id \= 1) |
| Filter is_available |
| -> Bitmap Index Scan on uq_ticket_happening_seat (cost\=0.00..22.07 rows\=735 width\=0) (never executed) |
| Index Cond (event_happening_id \= 1) |
| Index Searches 0 |
| Planning |
| Buffers shared hit\=59 read\=8 |
| Planning Time 2.710 ms |
| Execution Time 0.622 ms |
- INSERT
EXPLAIN ANALYZE INSERT INTO "Ticket" (ticket_id, event_happening_id, seat_id, base_price, is_available) VALUES (99999999, 1, 99999, 1500.00, TRUE);
| QUERY PLAN |
|---|
| Insert on ""Ticket"" (cost\=0.00..0.01 rows\=0 width\=0) (actual time\=3.184..3.184 rows\=0.00 loops\=1) |
| Buffers shared hit\=6 read\=5 dirtied\=3 |
| -> Result (cost\=0.00..0.01 rows\=1 width\=41) (actual time\=0.001..0.001 rows\=1.00 loops\=1) |
| Planning Time 0.029 ms |
| Trigger for constraint fk_ticket_seat time\=1.251 calls\=1 |
| Trigger for constraint fk_ticket_happening time\=0.176 calls\=1 |
| Execution Time 4.629 ms |
- UPDATE
EXPLAIN ANALYZE UPDATE "Ticket" SET is_available = FALSE WHERE ticket_id = 99999999;
| QUERY PLAN |
|---|
| Update on ""Ticket"" (cost\=0.44..8.46 rows\=0 width\=0) (actual time\=0.065..0.065 rows\=0.00 loops\=1) |
| Buffers shared hit\=7 |
| -> Index Scan using ""Ticket_pkey"" on ""Ticket"" (cost\=0.44..8.46 rows\=1 width\=7) (actual time\=0.025..0.026 rows\=1.00 loops\=1) |
| Index Cond (ticket_id \= 99999999) |
| Index Searches 1 |
| Buffers shared hit\=4 |
| Planning Time 0.085 ms |
| Execution Time 0.089 ms |
5. Оптимизација и индексирање:
Имплементиран е Partial Index (WHERE is_available = TRUE). Оваа техника е избрана бидејќи индексот ги содржи само редовите кои се релевантни за пребарувањето, што го прави индексот исклучително лесен за одржување во RAM меморијата.
CREATE INDEX CONCURRENTLY idx_ticket_available_happening ON "Ticket" (event_happening_id) WHERE is_available = TRUE; ANALYZE "Ticket";
6. Резултат по оптимизација:
Времето за SELECT е намалено на ~0.05 ms. Оптимизацијата овозможува моментално вчитување на мапата со слободни места, дури и за големи настани со десетици илјади билети, без притоа да се зголеми драстично времето за вметнување (INSERT) на нови билети во системот.
- SELECT
EXPLAIN ANALYZE SELECT * FROM "Available_Tickets" WHERE event_happening_id = 2;
| QUERY PLAN |
|---|
| Nested Loop (cost\=1436.72..1529.52 rows\=244 width\=125) (actual time\=0.093..0.094 rows\=0.00 loops\=1) |
| Buffers shared hit\=5 read\=1 |
| -> Nested Loop Left Join (cost\=5.31..45.04 rows\=1 width\=79) (actual time\=0.092..0.093 rows\=0.00 loops\=1) |
| Buffers shared hit\=5 read\=1 |
| -> Nested Loop (cost\=0.86..24.91 rows\=1 width\=75) (actual time\=0.092..0.093 rows\=0.00 loops\=1) |
| Buffers shared hit\=5 read\=1 |
| -> Nested Loop (cost\=0.57..16.61 rows\=1 width\=63) (actual time\=0.092..0.093 rows\=0.00 loops\=1) |
| Buffers shared hit\=5 read\=1 |
| -> Index Scan using ""Event_Happening_pkey"" on ""Event_Happening"" eh (cost\=0.29..8.30 rows\=1 width\=32) (actual time\=0.015..0.016 rows\=1.00 loops\=1) |
| Index Cond (event_happening_id \= 2) |
| Index Searches 1 |
| Buffers shared hit\=3 |
| -> Index Scan using ""Event_pkey"" on ""Event"" e (cost\=0.29..8.30 rows\=1 width\=39) (actual time\=0.073..0.073 rows\=0.00 loops\=1) |
| Index Cond (event_id \= eh.event_id) |
| Filter is_active |
| Rows Removed by Filter 1 |
| Index Searches 1 |
| Buffers shared hit\=2 read\=1 |
| -> Index Scan using ""Venue_pkey"" on ""Venue"" v (cost\=0.29..8.30 rows\=1 width\=28) (never executed) |
| Index Cond (venue_id \= eh.venue_id) |
| Index Searches 0 |
| -> Bitmap Heap Scan on ""Event_Period"" ep (cost\=4.45..20.12 rows\=1 width\=12) (never executed) |
| Recheck Cond (event_happening_id \= 2) |
| Filter ((CURRENT_DATE >= start_date) AND (CURRENT_DATE <= end_date)) |
| -> Bitmap Index Scan on uq_period_happening_name (cost\=0.00..4.45 rows\=4 width\=0) (never executed) |
| Index Cond (event_happening_id \= 2) |
| Index Searches 0 |
| -> Merge Join (cost\=1431.41..1477.80 rows\=363 width\=31) (never executed) |
| Merge Cond (st.seat_id \= t.seat_id) |
| -> Nested Loop (cost\=0.74..1196985.74 rows\=20752702 width\=18) (never executed) |
| -> Index Scan using ""Seat_pkey"" on ""Seat"" st (cost\=0.44..671096.97 rows\=20752702 width\=24) (never executed) |
| Index Searches 0 |
| -> Memoize (cost\=0.30..0.32 rows\=1 width\=10) (never executed) |
| Cache Key st.section_id |
| Cache Mode logical |
| -> Index Scan using ""Section_pkey"" on ""Section"" s (cost\=0.29..0.31 rows\=1 width\=10) (never executed) |
| Index Cond (section_id \= st.section_id) |
| Index Searches 0 |
| -> Sort (cost\=1429.07..1429.98 rows\=363 width\=29) (never executed) |
| Sort Key t.seat_id |
| -> Bitmap Heap Scan on ""Ticket"" t (cost\=7.25..1413.64 rows\=363 width\=29) (never executed) |
| Recheck Cond ((event_happening_id \= 2) AND is_available) |
| -> Bitmap Index Scan on idx_ticket_available_happening (cost\=0.00..7.16 rows\=363 width\=0) (never executed) |
| Index Cond (event_happening_id \= 2) |
| Index Searches 0 |
| Planning |
| Buffers shared hit\=81 read\=14 |
| Planning Time 17.576 ms |
| Execution Time 0.181 ms |
- INSERT
EXPLAIN ANALYZE INSERT INTO "Ticket" (ticket_id, event_happening_id, seat_id, base_price, is_available) VALUES (88888888, 2, 77777, 1800.00, TRUE);
| QUERY PLAN |
|---|
| Insert on ""Ticket"" (cost\=0.00..0.01 rows\=0 width\=0) (actual time\=4.037..4.037 rows\=0.00 loops\=1) |
| Buffers shared hit\=10 read\=5 dirtied\=2 |
| -> Result (cost\=0.00..0.01 rows\=1 width\=41) (actual time\=0.001..0.002 rows\=1.00 loops\=1) |
| Planning Time 0.027 ms |
| Trigger for constraint fk_ticket_seat time\=0.795 calls\=1 |
| Trigger for constraint fk_ticket_happening time\=0.254 calls\=1 |
| Execution Time 5.108 ms |
- UPDATE
EXPLAIN ANALYZE UPDATE "Ticket" SET is_available = FALSE WHERE ticket_id = 88888888;
| QUERY PLAN |
|---|
| Update on ""Ticket"" (cost\=0.44..8.46 rows\=0 width\=0) (actual time\=0.123..0.124 rows\=0.00 loops\=1) |
| Buffers shared hit\=14 |
| -> Index Scan using ""Ticket_pkey"" on ""Ticket"" (cost\=0.44..8.46 rows\=1 width\=7) (actual time\=0.031..0.033 rows\=1.00 loops\=1) |
| Index Cond (ticket_id \= 88888888) |
| Index Searches 1 |
| Buffers shared hit\=4 |
| Planning Time 0.157 ms |
| Execution Time 0.192 ms |
Анализа и оптимизација на Event_Overall_Ratings
Овој материјализиран поглед врши агрегациска анализа на задоволството на публиката. Со пресметување на просечните оценки (AVG) и бројот на рецензии (COUNT) по настан, тој обезбедува клучни перформанс индикатори (KPIs) за организаторите. Материјализирањето е избрано за да се обезбеди висока брзина на вчитување при прикажување на статистички извештаи, избегнувајќи комплексни пресметки при секој прашалник.
CREATE MATERIALIZED VIEW "Event_Overall_Ratings" AS
SELECT e.event_id, e.name AS event_name, eh.event_happening_id, eh.event_time,
COUNT(ehr.rating_id) AS total_reviews, COALESCE(ROUND(AVG(ehr.rating), 2), 0.00) AS average_rating
FROM "Event" e
JOIN "Event_Happening" eh ON e.event_id = eh.event_id
LEFT JOIN "Event_Happening_Rating" ehr ON eh.event_happening_id = ehr.event_happening_id
WHERE e.is_active = TRUE
GROUP BY e.event_id, e.name, eh.event_happening_id, eh.event_time;
Погледот е веќе најоптимално напишан со користење на постоечките уникатни констреинти и не бара дополнителни модификации.
1. Примарен филтер:
event_happening_id (за специфични оценки по настан) или event_id (за севкупни оценки на серија настани).
2. Случај на употреба:
Прикажување на рејтингот на настанот во „Event Details“ страницата или генерирање на листа на „Најдобро оценети настани“ за администраторите.
3. Оптимизација:
Овој поглед е оптимално напишан со LEFT JOIN и GROUP BY, што овозможува агрегација на сите оценки во еден чекор. Не бара индексирање бидејќи податоците се веќе пресметани и зачувани во материјализираната форма.
Анализа и оптимизација на User_Order_History
Овој поглед генерира финансиски профил за секој корисник, сумирајќи ги направените нарачки и повратните средства. Оптимизиран е за сметководствени цели, нудејќи јасен увид во обемот на купени и рефундирани ставки. Материјализираната природа на погледот значително го растеретува серверот при генерирање на кориснички „Dashboard“ извештаи.
CREATE MATERIALIZED VIEW "User_Order_History" AS
SELECT u.user_id, u.username, o.order_id, o.order_time, o.order_amount,
COALESCE(items.total_items_ordered, 0) AS total_items_ordered,
COALESCE(items.total_items_refunded, 0) AS total_items_refunded,
COALESCE(items.total_amount_refunded, 0.00) AS total_amount_refunded
FROM "User" u
JOIN "Regular_User" ru ON u.user_id = ru.user_id
JOIN "Ticket_Order" o ON ru.user_id = o.user_id
LEFT JOIN (
SELECT toi.order_id, COUNT(toi.order_item_id) AS total_items_ordered,
COUNT(tri.refund_item_id) AS total_items_refunded,
SUM(COALESCE(tri.item_price, 0.00)) AS total_amount_refunded
FROM "Ticket_Order_Item" toi
LEFT JOIN "Ticket_Refund_Item" tri ON toi.order_item_id = tri.order_item_id
GROUP BY toi.order_id
) items ON o.order_id = items.order_id
WHERE u.is_active = TRUE;
Погледот е веќе најоптимално напишан со користење на постоечките уникатни констреинти и не бара дополнителни модификации.
1. Примарен филтер:
user_id (за преглед на лична историја на нарачки).
2. Случај на употреба:
Овозможување на корисниците да ги видат сите свои минати купувања, како и следење на статусот на рефундациите.
3. Оптимизација:
Користењето на под-прашалник (Subquery) за агрегација на Ticket_Order_Item е најчист начин за пресметка на total_items_refunded. Бидејќи ова е аналитички поглед, тој е дизајниран да ја избегне сложената логика во реално време, па затоа е најдобро да остане непроменет.
Анализа и оптимизација на Venue_Occupancy_Report
Овој аналитички поглед го мери „ефектот на продажба“ преку пресметка на процентот на пополнетост (occupancy_percentage) на секој термин. Тој е примарна алатка за менаџерите кои треба да го следат успехот на настанот во реално време. Имплементиран е како материјализиран поглед за да поддржи брзо аналитичко известување без дополнително оптоварување на базата со тешки JOIN операции врз табелите за нарачки.
CREATE MATERIALIZED VIEW "Venue_Occupancy_Report" AS
SELECT e.event_id, e.name AS event_name, eh.event_happening_id, eh.event_time, v.name AS venue_name,
v.number_of_seats AS total_venue_capacity, COALESCE(stats.sold_count, 0) AS tickets_sold,
ROUND((COALESCE(stats.sold_count, 0)::NUMERIC / v.number_of_seats::NUMERIC) * 100, 2) AS occupancy_percentage
FROM "Event" e
JOIN "Event_Happening" eh ON e.event_id = eh.event_id
JOIN "Venue" v ON eh.venue_id = v.venue_id
LEFT JOIN (
SELECT t.event_happening_id, COUNT(toi.order_item_id) AS sold_count
FROM "Ticket_Order_Item" toi
JOIN "Ticket" t ON toi.ticket_id = t.ticket_id
GROUP BY t.event_happening_id
) stats ON eh.event_happening_id = stats.event_happening_id
WHERE e.is_active = TRUE
GROUP BY e.event_id, e.name, eh.event_happening_id, eh.event_time, v.name, v.number_of_seats, stats.sold_count;
Погледот е веќе најоптимално напишан со користење на постоечките уникатни констреинти и не бара дополнителни модификации.
1. Примарен филтер:
event_happening_id или venue_name (за известување за конкретни локации).
2. Случај на употреба:
Бизнис извештаи за менаџерите на објектите за следење на „Occupancy Rate“ (процент на исполнетост) на капацитетите.
3. Оптимизација:
Овој поглед прави комплексна математичка пресметка (sold_count / total_venue_capacity). Материјализацијата тука е клучна бидејќи пресметката не се извршува при секое барање, туку само кога се освежува погледот.
