Оптимизација на прашалници
Овој поглед дава преглед на сите изведувачи и настаните на кои тие учествуваат, заедно со прецизниот термин на нивниот настап.
CREATE VIEW "Performer_Events" AS
SELECT p.performer_id,
p.name AS performer_name,
e.event_id,
e.name AS event_name,
eh.event_time
FROM "Performer" p
JOIN "Event_Happening_Performer" ehp ON p.performer_id = ehp.performer_id
JOIN "Event_Happening" eh ON ehp.event_happening_id = eh.event_happening_id
JOIN "Event" e ON eh.event_id = e.event_id;
1. Примарен филтер:
Примарен филтер за овој поглед ќе биде според performer_id (ID на изведувач), бидејќи најчестото пребарување е за да се видат настапите на конкретен изведувач. Дополнително, ќе може да се пребарува и по event_id.
2. Случај на употреба:
Овој поглед е клучен за корисничкиот дел од апликацијата, каде што корисниците пребаруваат каде и кога настапува нивниот омилен изведувач. Бидејќи ова е акција што често се повторува од многу корисници истовремено, перформансите мора да бидат на високо ниво за да се обезбеди добро корисничко искуство.
3. Иницијално време:
Иницијалното време за извршување на погледот изнесува 3.249s (3249ms). Ова е исклучително бавно и неприфатливо за една веб-апликација, каде што секој одзив над 500ms се смета за критичен.
4. Анализа на планот на извршување:
Иако базата се обидува да користи постоечки Primary Key индекси, главниот проблем се јавува во спојувањето на четирите табели Performer, Event_Happening_Performer, Event_Happening, Event.
Најбавниот дел е кај Nested Loop операциите и проверката на Foreign Keys при INSERT (1.424s).
Времето на планирање е исто така високо (1.8s), што укажува на комплексност во резолвирањето на релациите без соодветни патеки.
EXPLAIN ANALYZE
SELECT * FROM "Performer_Events" WHERE performer_id = 10;
| QUERY PLAN
|
|---|
| Nested Loop (cost\=1.14..73.38 rows\=7 width\=68) (actual time\=1505.862..3249.242 rows\=4 loops\=1)
|
| -> Nested Loop (cost\=0.86..70.92 rows\=7 width\=37) (actual time\=1079.648..2259.465 rows\=4 loops\=1)
|
| -> Index Scan using "Performer_pkey" on "Performer" p (cost\=0.28..8.30 rows\=1 width\=21) (actual time\=701.003..701.005 rows\=1 loops\=1)
|
| Index Cond: (performer_id \= 10)
|
| -> Nested Loop (cost\=0.57..62.55 rows\=7 width\=24) (actual time\=378.637..1558.442 rows\=4 loops\=1)
|
| -> Index Only Scan using uq_performer_at_time on "Event_Happening_Performer" ehp (cost\=0.29..4.41 rows\=7 width\=16) (actual time\=90.817..90.838 rows\=4 loops\=1)
|
| Index Cond: (performer_id \= 10)
|
| Heap Fetches: 0
|
| -> Index Scan using "Event_Happening_pkey" on "Event_Happening" eh (cost\=0.29..8.30 rows\=1 width\=24) (actual time\=366.888..366.889 rows\=1 loops\=4)
|
| Index Cond: (event_happening_id \= ehp.event_happening_id)
|
| -> Index Scan using "Event_pkey" on "Event" e (cost\=0.29..0.35 rows\=1 width\=39) (actual time\=247.432..247.432 rows\=1 loops\=4)
|
| Index Cond: (event_id \= eh.event_id)
|
| Planning Time: 1816.633 ms
|
| Execution Time: 3249.314 ms
|
EXPLAIN ANALYZE
INSERT INTO "Event_Happening_Performer" (event_happening_id, performer_id)
VALUES (1, 10);
| QUERY PLAN
|
|---|
| Insert on "Event_Happening_Performer" (cost\=0.00..0.01 rows\=0 width\=0) (actual time\=558.243..558.244 rows\=0 loops\=1)
|
| -> Result (cost\=0.00..0.01 rows\=1 width\=16) (actual time\=0.001..0.002 rows\=1 loops\=1)
|
| Planning Time: 0.042 ms
|
| Trigger for constraint fk_ehp_event_happening: time\=826.105 calls\=1
|
| Trigger for constraint fk_ehp_performer: time\=40.075 calls\=1
|
| Execution Time: 1424.451 ms
|
EXPLAIN ANALYZE
UPDATE "Event_Happening_Performer"
SET performer_id = 11
WHERE event_happening_id = 1 AND performer_id = 10;
| QUERY PLAN
|
|---|
| Update on "Event_Happening_Performer" (cost\=0.29..8.31 rows\=0 width\=0) (actual time\=0.219..0.219 rows\=0 loops\=1)
|
| -> Index Scan using uq_performer_at_time on "Event_Happening_Performer" (cost\=0.29..8.31 rows\=1 width\=14) (actual time\=0.120..0.121 rows\=1 loops\=1)
|
| Index Cond: ((performer_id \= 10) AND (event_happening_id \= 1))
|
| Planning Time: 0.139 ms
|
| Trigger for constraint fk_ehp_performer: time\=0.279 calls\=1
|
| Execution Time: 23.621 ms
|
Погледот е бавен поради комплексни врски помеѓи четири табели што резултира со време од 3.2 s. Индексите на performer_id и event_id го елиминираат целосното скенирање на табелите и овозможуваат инстантно поврзување на изведувачите со нивните настани.
5. Оптимизација и индексирање:
За да го решиме проблемот, воведуваме индекси на колоните кои служат за поврзување во JOIN условите и во WHERE филтрите:
idx_ehp_performer_id и idx_ehp_happening_id на табелата што ги поврзува изведувачите со термините.
idx_event_happening_event_id за побрзо поврзување на конкретниот термин со името на настанот.
-- indexes for linking performers with specific event occurrences (M:N relationship)
CREATE INDEX idx_ehp_performer_id ON "Event_Happening_Performer"(performer_id);
CREATE INDEX idx_ehp_happening_id ON "Event_Happening_Performer"(event_happening_id);
-- index for optimizing event lookups within scheduled event happenings
CREATE INDEX idx_event_happening_event_id ON "Event_Happening"(event_id);
6. Резултат по оптимизација:
По додавањето на индексите, времето на извршување на SELECT се намали на 0.533ms.
Подобрување: Ова е забрзување од над 6000 пати.
Операциите за INSERT и UPDATE сега се извршуваат за помалку од 1ms, што значи дека индексите не го забавуваат системот, туку помагаат дури и кај тригерите за Foreign Keys.
EXPLAIN ANALYZE
SELECT * FROM "Performer_Events" WHERE performer_id = 10;
| QUERY PLAN
|
|---|
| Nested Loop (cost\=1.14..77.38 rows\=7 width\=68) (actual time\=0.251..0.479 rows\=4 loops\=1)
|
| -> Nested Loop (cost\=0.86..74.92 rows\=7 width\=37) (actual time\=0.201..0.306 rows\=4 loops\=1)
|
| -> Index Scan using "Performer_pkey" on "Performer" p (cost\=0.28..8.30 rows\=1 width\=21) (actual time\=0.093..0.094 rows\=1 loops\=1)
|
| Index Cond: (performer_id \= 10)
|
| -> Nested Loop (cost\=0.57..66.55 rows\=7 width\=24) (actual time\=0.105..0.207 rows\=4 loops\=1)
|
| -> Index Only Scan using uq_performer_at_time on "Event_Happening_Performer" ehp (cost\=0.29..8.41 rows\=7 width\=16) (actual time\=0.062..0.065 rows\=4 loops\=1)
|
| Index Cond: (performer_id \= 10)
|
| Heap Fetches: 1
|
| -> Index Scan using "Event_Happening_pkey" on "Event_Happening" eh (cost\=0.29..8.30 rows\=1 width\=24) (actual time\=0.032..0.032 rows\=1 loops\=4)
|
| Index Cond: (event_happening_id \= ehp.event_happening_id)
|
| -> Index Scan using "Event_pkey" on "Event" e (cost\=0.29..0.35 rows\=1 width\=39) (actual time\=0.041..0.041 rows\=1 loops\=4)
|
| Index Cond: (event_id \= eh.event_id)
|
| Planning Time: 1.600 ms
|
| Execution Time: 0.533 ms
|
EXPLAIN ANALYZE
INSERT INTO "Event_Happening_Performer" (event_happening_id, performer_id)
VALUES (2, 10);
| QUERY PLAN
|
|---|
| Insert on "Event_Happening_Performer" (cost\=0.00..0.01 rows\=0 width\=0) (actual time\=0.307..0.308 rows\=0 loops\=1)
|
| -> Result (cost\=0.00..0.01 rows\=1 width\=16) (actual time\=0.001..0.001 rows\=1 loops\=1)
|
| Planning Time: 0.046 ms
|
| Trigger for constraint fk_ehp_event_happening: time\=0.332 calls\=1
|
| Trigger for constraint fk_ehp_performer: time\=0.169 calls\=1
|
| Execution Time: 0.833 ms
|
EXPLAIN ANALYZE
UPDATE "Event_Happening_Performer"
SET performer_id = 12
WHERE event_happening_id = 2 AND performer_id = 10;
| QUERY PLAN
|
|---|
| Update on "Event_Happening_Performer" (cost\=0.29..8.31 rows\=0 width\=0) (actual time\=0.437..0.438 rows\=0 loops\=1)
|
| -> Index Scan using idx_ehp_happening_id on "Event_Happening_Performer" (cost\=0.29..8.31 rows\=1 width\=14) (actual time\=0.174..0.175 rows\=1 loops\=1)
|
| Index Cond: (event_happening_id \= 2)
|
| Filter: (performer_id \= 10)
|
| Rows Removed by Filter: 1
|
| Planning Time: 0.161 ms
|
| Trigger for constraint fk_ehp_performer: time\=0.271 calls\=1
|
| Execution Time: 0.763 ms
|
Анализа и оптимизација на Venue_Layout
Овој поглед ја прикажува целосната структура на секој објект (сала), поврзувајќи ги поединечните седишта со соодветните сектори и името на објектот.
CREATE 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.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;
Без индекс:
EXPLAIN ANALYZE
SELECT * FROM "Venue_Layout" WHERE venue_id = 1;
| QUERY PLAN
|
|---|
| Nested Loop (cost\=1001.14..10398.19 rows\=2264 width\=55) (actual time\=447.576..2164.258 rows\=775 loops\=1)
|
| -> Index Scan using "Venue_pkey" on "Venue" v (cost\=0.29..8.30 rows\=1 width\=28) (actual time\=68.551..68.555 rows\=1 loops\=1)
|
| Index Cond: (venue_id \= 1)
|
| -> Gather (cost\=1000.85..10367.25 rows\=2264 width\=35) (actual time\=379.017..2095.610 rows\=775 loops\=1)
|
| Workers Planned: 1
|
| Workers Launched: 1
|
| -> Nested Loop (cost\=0.85..9140.85 rows\=1332 width\=35) (actual time\=932.279..1787.769 rows\=388 loops\=2)
|
| -> Parallel Index Scan using "Section_pkey" on "Section" s (cost\=0.29..1691.80 rows\=4 width\=23) (actual time\=771.383..1395.534 rows\=2 loops\=2)
|
| Filter: (venue_id \= 1)
|
| Rows Removed by Filter: 27502
|
| -> Index Scan using uq_seat_section_number on "Seat" st (cost\=0.56..1853.21 rows\=905 width\=20) (actual time\=64.373..156.866 rows\=155 loops\=5)
|
| Index Cond: (section_id \= s.section_id)
|
| Planning Time: 1874.048 ms
|
| Execution Time: 2164.361 ms
|
EXPLAIN ANALYZE
INSERT INTO "Seat" (seat_id, section_id, seat_number)
SELECT COALESCE(MAX(seat_id), 0) + 1, 1, 999999 FROM "Seat";
| QUERY PLAN
|
|---|
| Insert on "Seat" (cost\=0.67..0.69 rows\=0 width\=0) (actual time\=745.461..745.464 rows\=0 loops\=1)
|
| -> Subquery Scan on "*SELECT*" (cost\=0.67..0.69 rows\=1 width\=20) (actual time\=481.921..481.925 rows\=1 loops\=1)
|
| -> Result (cost\=0.67..0.69 rows\=1 width\=16) (actual time\=481.918..481.920 rows\=1 loops\=1)
|
| InitPlan 1
|
| -> Limit (cost\=0.56..0.67 rows\=1 width\=8) (actual time\=481.909..481.910 rows\=1 loops\=1)
|
| -> Index Only Scan Backward using "Seat_pkey" on "Seat" "Seat_1" (cost\=0.56..2330912.81 rows\=20753208 width\=8) (actual time\=481.907..481.908 rows\=1 loops\=1)
|
| Heap Fetches: 0
|
| Planning Time: 0.339 ms
|
| Trigger for constraint fk_seat_section: time\=0.533 calls\=1
|
| Execution Time: 746.039 ms
|
EXPLAIN ANALYZE
UPDATE "Seat"
SET seat_number = 888888
WHERE seat_number = 999999;
| QUERY PLAN
|
|---|
| Update on "Seat" (cost\=0.00..391602.10 rows\=0 width\=0) (actual time\=308799.033..308799.035 rows\=0 loops\=1)
|
| -> Seq Scan on "Seat" (cost\=0.00..391602.10 rows\=11492 width\=10) (actual time\=308792.032..308792.034 rows\=1 loops\=1)
|
| Filter: (seat_number \= 999999)
|
| Rows Removed by Filter: 20753209
|
| Planning Time: 0.114 ms
|
| JIT:
|
| Functions: 4
|
| Options: Inlining false, Optimization false, Expressions true, Deforming true
|
| Timing: Generation 0.405 ms (Deform 0.071 ms), Inlining 0.000 ms, Optimization 0.595 ms, Emission 6.566 ms, Total 7.565 ms
|
| Execution Time: 312239.424 ms
|
Времето за ажурирање од 312 s е неприфатливо за интеракција со мапа на седишта во реално време. Со поставување индекси на seat_number и venue_id, пребарувањето и промената на статусот на седиштата се извршуваат за милисекунди наместо за неколку минути.
Оптимизација:
-- index for linking seats to their respective sections
CREATE INDEX idx_seat_section_id ON "Seat"(section_id);
-- index for linking sections to venues
CREATE INDEX idx_section_venue_id ON "Section"(venue_id);
-- index for optimizing search and update operations on specific seat numbers
CREATE INDEX idx_seat_number ON "Seat"(seat_number);
Со индекс:
EXPLAIN ANALYZE
SELECT * FROM "Venue_Layout" WHERE venue_id = 1;
| QUERY PLAN
|
|---|
| Nested Loop (cost\=1.01..3060.48 rows\=2264 width\=55) (actual time\=0.184..0.512 rows\=776 loops\=1)
|
| -> Nested Loop (cost\=0.57..16.76 rows\=6 width\=43) (actual time\=0.140..0.144 rows\=5 loops\=1)
|
| -> Index Scan using "Venue_pkey" on "Venue" v (cost\=0.29..8.30 rows\=1 width\=28) (actual time\=0.077..0.078 rows\=1 loops\=1)
|
| Index Cond: (venue_id \= 1)
|
| -> Index Scan using idx_section_venue_id on "Section" s (cost\=0.29..8.39 rows\=6 width\=23) (actual time\=0.058..0.060 rows\=5 loops\=1)
|
| Index Cond: (venue_id \= 1)
|
| -> Index Scan using idx_seat_section_id on "Seat" st (cost\=0.44..498.24 rows\=905 width\=20) (actual time\=0.012..0.053 rows\=155 loops\=5)
|
| Index Cond: (section_id \= s.section_id)
|
| Planning Time: 1.235 ms
|
| Execution Time: 0.572 ms
|
EXPLAIN ANALYZE
INSERT INTO "Seat" (seat_id, section_id, seat_number)
SELECT COALESCE(MAX(seat_id), 0) + 1, 1, 111222 FROM "Seat";
| QUERY PLAN
|
|---|
| Insert on "Seat" (cost\=0.67..0.69 rows\=0 width\=0) (actual time\=0.493..0.494 rows\=0 loops\=1)
|
| -> Subquery Scan on "*SELECT*" (cost\=0.67..0.69 rows\=1 width\=20) (actual time\=0.169..0.171 rows\=1 loops\=1)
|
| -> Result (cost\=0.67..0.69 rows\=1 width\=16) (actual time\=0.167..0.168 rows\=1 loops\=1)
|
| InitPlan 1
|
| -> Limit (cost\=0.56..0.67 rows\=1 width\=8) (actual time\=0.162..0.163 rows\=1 loops\=1)
|
| -> Index Only Scan Backward using "Seat_pkey" on "Seat" "Seat_1" (cost\=0.56..2330912.84 rows\=20753210 width\=8) (actual time\=0.161..0.161 rows\=1 loops\=1)
|
| Heap Fetches: 1
|
| Planning Time: 0.219 ms
|
| Trigger for constraint fk_seat_section: time\=0.300 calls\=1
|
| Execution Time: 0.839 ms
|
EXPLAIN ANALYZE
UPDATE "Seat"
SET seat_number = 333444
WHERE seat_number = 111222;
| QUERY PLAN
|
|---|
| Update on "Seat" (cost\=0.44..44297.09 rows\=0 width\=0) (actual time\=0.395..0.396 rows\=0 loops\=1)
|
| -> Index Scan using idx_seat_number on "Seat" (cost\=0.44..44297.09 rows\=11492 width\=10) (actual time\=0.193..0.195 rows\=1 loops\=1)
|
| Index Cond: (seat_number \= 111222)
|
| Planning Time: 0.141 ms
|
| Execution Time: 0.461 ms
|
Анализа и оптимизација на User_Tickets
Овој поглед ја прикажува целосната структура на секој објект (сала), поврзувајќи ги поединечните седишта со соодветните сектори и името на објектот.
CREATE VIEW "User_Tickets" AS
SELECT u.user_id,
u.username,
t.ticket_id,
e.event_id,
e.name AS event_name,
tp.purchase_id,
tp.qr_code,
tr.refund_id,
tr.refund_time
FROM "User" u
JOIN "Ticket_Purchase" tp ON u.user_id = tp.user_id
JOIN "Ticket" t ON tp.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" tr ON tp.purchase_id = tr.purchase_id;
Без индекс:
EXPLAIN ANALYZE
SELECT * FROM "User_Tickets" WHERE user_id = 1;
| QUERY PLAN
|
|---|
| Nested Loop Left Join (cost\=1002.00..232798.26 rows\=1 width\=115) (actual time\=431.345..441.975 rows\=1 loops\=1)
|
| -> Nested Loop (cost\=1001.56..232789.81 rows\=1 width\=99) (actual time\=431.312..441.941 rows\=1 loops\=1)
|
| -> Nested Loop (cost\=1001.28..232789.45 rows\=1 width\=68) (actual time\=431.253..441.878 rows\=1 loops\=1)
|
| -> Nested Loop (cost\=1000.99..232789.15 rows\=1 width\=68) (actual time\=431.200..441.822 rows\=1 loops\=1)
|
| -> Nested Loop (cost\=1000.43..232780.57 rows\=1 width\=60) (actual time\=431.038..441.657 rows\=1 loops\=1)
|
| -> Index Scan using "User_pkey" on "User" u (cost\=0.43..8.45 rows\=1 width\=26) (actual time\=0.114..0.118 rows\=1 loops\=1)
|
| Index Cond: (user_id \= 1)
|
| -> Gather (cost\=1000.00..232772.11 rows\=1 width\=42) (actual time\=430.826..441.440 rows\=1 loops\=1)
|
| Workers Planned: 4
|
| Workers Launched: 4
|
| -> Parallel Seq Scan on "Ticket_Purchase" tp (cost\=0.00..231772.01 rows\=1 width\=42) (actual time\=389.356..389.357 rows\=0 loops\=5)
|
| Filter: (user_id \= 1)
|
| Rows Removed by Filter: 3200000
|
| -> Index Scan using "Ticket_pkey" on "Ticket" t (cost\=0.56..8.58 rows\=1 width\=16) (actual time\=0.104..0.105 rows\=1 loops\=1)
|
| Index Cond: (ticket_id \= tp.ticket_id)
|
| -> Index Scan using "Event_Happening_pkey" on "Event_Happening" eh (cost\=0.29..0.31 rows\=1 width\=16) (actual time\=0.033..0.034 rows\=1 loops\=1)
|
| Index Cond: (event_happening_id \= t.event_happening_id)
|
| -> Index Scan using "Event_pkey" on "Event" e (cost\=0.29..0.35 rows\=1 width\=39) (actual time\=0.039..0.040 rows\=1 loops\=1)
|
| Index Cond: (event_id \= eh.event_id)
|
| -> Index Scan using "Ticket_Refund_purchase_id_key" on "Ticket_Refund" tr (cost\=0.43..8.45 rows\=1 width\=24) (actual time\=0.015..0.016 rows\=0 loops\=1)
|
| Index Cond: (purchase_id \= tp.purchase_id)
|
| Planning Time: 15.360 ms
|
| JIT:
|
| Functions: 44
|
| Options: Inlining false, Optimization false, Expressions true, Deforming true
|
| Timing: Generation 3.367 ms (Deform 1.376 ms), Inlining 0.000 ms, Optimization 2.363 ms, Emission 42.753 ms, Total 48.484 ms
|
| Execution Time: 443.784 ms
|
EXPLAIN ANALYZE
INSERT INTO "Ticket_Purchase" (purchase_id, ticket_id, user_id, qr_code, purchase_amount)
SELECT COALESCE(MAX(purchase_id), 0) + 1, 1, 1, 'QR-TEST-CODE-001', 1200.00
FROM "Ticket_Purchase";
| QUERY PLAN
|
|---|
| Insert on "Ticket_Purchase" (cost\=0.47..0.50 rows\=0 width\=0) (actual time\=0.217..0.218 rows\=0 loops\=1)
|
| -> Subquery Scan on "*SELECT*" (cost\=0.47..0.50 rows\=1 width\=552) (actual time\=0.055..0.057 rows\=1 loops\=1)
|
| -> Result (cost\=0.47..0.48 rows\=1 width\=80) (actual time\=0.043..0.044 rows\=1 loops\=1)
|
| InitPlan 1
|
| -> Limit (cost\=0.43..0.47 rows\=1 width\=8) (actual time\=0.038..0.039 rows\=1 loops\=1)
|
| -> Index Only Scan Backward using "Ticket_Purchase_pkey" on "Ticket_Purchase" "Ticket_Purchase_1" (cost\=0.43..484152.95 rows\=16000004 width\=8) (actual time\=0.037..0.038 rows\=1 loops\=1)
|
| Heap Fetches: 3
|
| Planning Time: 0.207 ms
|
| Trigger for constraint fk_purchase_ticket: time\=0.190 calls\=1
|
| Trigger for constraint fk_purchase_user: time\=0.136 calls\=1
|
| Execution Time: 0.600 ms
|
EXPLAIN ANALYZE
UPDATE "Ticket_Purchase"
SET purchase_amount = 1500.00
WHERE user_id = 1 AND qr_code = 'QR-TEST-CODE-001';
| QUERY PLAN
|
|---|
| Update on "Ticket_Purchase" (cost\=0.56..8.58 rows\=0 width\=0) (actual time\=0.121..0.122 rows\=0 loops\=1)
|
| -> Index Scan using "Ticket_Purchase_qr_code_key" on "Ticket_Purchase" (cost\=0.56..8.58 rows\=1 width\=10) (actual time\=0.048..0.050 rows\=1 loops\=1)
|
| Index Cond: ((qr_code)::text \= 'QR-TEST-CODE-001'::text)
|
| Filter: (user_id \= 1)
|
| Planning Time: 0.181 ms
|
| Execution Time: 0.157 ms
|
Приказот на историјата на билети трае 251.9 s, што го блокира корисничкиот профил. Индексот на user_id овозможува базата веднаш да ги лоцира билетите на конкретниот корисник без да ги пребарува сите трансакции во системот.
Оптимизација:
-- index for linking ticket purchases to the specific tickets
CREATE INDEX idx_ticket_purchase_ticket_id ON "Ticket_Purchase"(ticket_id);
-- index for linking purchases to users
CREATE INDEX idx_ticket_purchase_user_id ON "Ticket_Purchase"(user_id);
-- index for the LEFT JOIN with refunds
CREATE INDEX idx_ticket_refund_purchase_id ON "Ticket_Refund"(purchase_id);
-- index for optimizing event lookups within scheduled event happenings
CREATE INDEX idx_event_happening_event_id ON "Event_Happening"(event_id);
Со индекс:
EXPLAIN ANALYZE
SELECT * FROM "User_Tickets" WHERE user_id = 1;
| QUERY PLAN
|
|---|
| Nested Loop Left Join (cost\=1002.00..232798.25 rows\=1 width\=115) (actual time\=438.622..449.169 rows\=2 loops\=1)
|
| -> Nested Loop (cost\=1001.56..232789.80 rows\=1 width\=99) (actual time\=438.588..449.128 rows\=2 loops\=1)
|
| -> Gather (cost\=1001.13..232781.35 rows\=1 width\=81) (actual time\=438.383..448.900 rows\=2 loops\=1)
|
| Workers Planned: 4
|
| Workers Launched: 4
|
| -> Nested Loop (cost\=1.14..231781.25 rows\=1 width\=81) (actual time\=397.225..397.235 rows\=0 loops\=5)
|
| -> Nested Loop (cost\=0.85..231780.89 rows\=1 width\=50) (actual time\=397.203..397.211 rows\=0 loops\=5)
|
| -> Nested Loop (cost\=0.56..231780.59 rows\=1 width\=50) (actual time\=397.176..397.182 rows\=0 loops\=5)
|
| -> Parallel Seq Scan on "Ticket_Purchase" tp (cost\=0.00..231772.01 rows\=1 width\=42) (actual time\=397.114..397.115 rows\=0 loops\=5)
|
| Filter: (user_id \= 1)
|
| Rows Removed by Filter: 3200000
|
| -> Index Scan using "Ticket_pkey" on "Ticket" t (cost\=0.56..8.58 rows\=1 width\=16) (actual time\=0.107..0.107 rows\=1 loops\=2)
|
| Index Cond: (ticket_id \= tp.ticket_id)
|
| -> Index Scan using "Event_Happening_pkey" on "Event_Happening" eh (cost\=0.29..0.31 rows\=1 width\=16) (actual time\=0.055..0.055 rows\=1 loops\=2)
|
| Index Cond: (event_happening_id \= t.event_happening_id)
|
| -> Index Scan using "Event_pkey" on "Event" e (cost\=0.29..0.35 rows\=1 width\=39) (actual time\=0.044..0.044 rows\=1 loops\=2)
|
| Index Cond: (event_id \= eh.event_id)
|
| -> Index Scan using "User_pkey" on "User" u (cost\=0.43..8.45 rows\=1 width\=26) (actual time\=0.086..0.087 rows\=1 loops\=2)
|
| Index Cond: (user_id \= 1)
|
| -> Index Scan using idx_ticket_refund_purchase_id on "Ticket_Refund" tr (cost\=0.43..8.45 rows\=1 width\=24) (actual time\=0.010..0.010 rows\=0 loops\=2)
|
| Index Cond: (purchase_id \= tp.purchase_id)
|
| Planning Time: 2.459 ms
|
| JIT:
|
| Functions: 99
|
| Options: Inlining false, Optimization false, Expressions true, Deforming true
|
| Timing: Generation 7.544 ms (Deform 3.336 ms), Inlining 0.000 ms, Optimization 3.874 ms, Emission 71.829 ms, Total 83.247 ms
|
| Execution Time: 450.880 ms
|
EXPLAIN ANALYZE
INSERT INTO "Ticket_Purchase" (purchase_id, ticket_id, user_id, qr_code, purchase_amount)
SELECT COALESCE(MAX(purchase_id), 0) + 1, 1, 1, 'QR-TEST-CODE-002', 1200.00
FROM "Ticket_Purchase";
| QUERY PLAN
|
|---|
| Insert on "Ticket_Purchase" (cost\=0.47..0.50 rows\=0 width\=0) (actual time\=0.452..0.453 rows\=0 loops\=1)
|
| -> Subquery Scan on "*SELECT*" (cost\=0.47..0.50 rows\=1 width\=552) (actual time\=0.049..0.051 rows\=1 loops\=1)
|
| -> Result (cost\=0.47..0.48 rows\=1 width\=80) (actual time\=0.038..0.039 rows\=1 loops\=1)
|
| InitPlan 1
|
| -> Limit (cost\=0.43..0.47 rows\=1 width\=8) (actual time\=0.033..0.033 rows\=1 loops\=1)
|
| -> Index Only Scan Backward using "Ticket_Purchase_pkey" on "Ticket_Purchase" "Ticket_Purchase_1" (cost\=0.43..484152.93 rows\=16000003 width\=8) (actual time\=0.032..0.032 rows\=1 loops\=1)
|
| Heap Fetches: 2
|
| Planning Time: 0.207 ms
|
| Trigger for constraint fk_purchase_ticket: time\=0.194 calls\=1
|
| Trigger for constraint fk_purchase_user: time\=0.130 calls\=1
|
| Execution Time: 0.828 ms
|
EXPLAIN ANALYZE
UPDATE "Ticket_Purchase"
SET purchase_amount = 1350.00
WHERE user_id = 1 AND qr_code = 'QR-TEST-CODE-002';
| QUERY PLAN
|
|---|
| Update on "Ticket_Purchase" (cost\=0.56..8.58 rows\=0 width\=0) (actual time\=0.207..0.207 rows\=0 loops\=1)
|
| -> Index Scan using "Ticket_Purchase_qr_code_key" on "Ticket_Purchase" (cost\=0.56..8.58 rows\=1 width\=10) (actual time\=0.124..0.126 rows\=1 loops\=1)
|
| Index Cond: ((qr_code)::text \= 'QR-TEST-CODE-002'::text)
|
| Filter: (user_id \= 1)
|
| Planning Time: 0.150 ms
|
| Execution Time: 0.267 ms
|
Анализа и оптимизација на Event_User_Ratings
Овој поглед овозможува детален пристап до поединечните коментари и оценки што секој корисник ги оставил за одреден термин на настан.
CREATE VIEW "Event_User_Ratings" AS
SELECT eh.event_happening_id,
e.event_id,
e.name AS event_name,
u.user_id,
u.username,
ehr.rating_id,
ehr.rating,
ehr.comment
FROM "Event" e
JOIN "Event_Happening" eh ON e.event_id = eh.event_id
JOIN "Event_Happening_Rating" ehr ON eh.event_happening_id = ehr.event_happening_id
JOIN "User" u ON ehr.user_id = u.user_id;
Без индекс:
EXPLAIN ANALYZE
SELECT * FROM "Event_User_Ratings"
WHERE user_id = 1;
| QUERY PLAN
|
|---|
| Nested Loop (cost\=1.29..21.42 rows\=1 width\=131) (actual time\=0.104..0.105 rows\=0 loops\=1)
|
| -> Nested Loop (cost\=0.86..12.96 rows\=1 width\=113) (actual time\=0.103..0.105 rows\=0 loops\=1)
|
| -> Nested Loop (cost\=0.57..12.61 rows\=1 width\=82) (actual time\=0.103..0.104 rows\=0 loops\=1)
|
| -> Index Scan using idx_ehr_user_id on "Event_Happening_Rating" ehr (cost\=0.29..4.30 rows\=1 width\=74) (actual time\=0.103..0.103 rows\=0 loops\=1)
|
| Index Cond: (user_id \= 1)
|
| -> Index Scan using "Event_Happening_pkey" on "Event_Happening" eh (cost\=0.29..8.30 rows\=1 width\=16) (never executed)
|
| Index Cond: (event_happening_id \= ehr.event_happening_id)
|
| -> Index Scan using "Event_pkey" on "Event" e (cost\=0.29..0.35 rows\=1 width\=39) (never executed)
|
| Index Cond: (event_id \= eh.event_id)
|
| -> Index Scan using "User_pkey" on "User" u (cost\=0.43..8.45 rows\=1 width\=26) (never executed)
|
| Index Cond: (user_id \= 1)
|
| Planning Time: 464.594 ms
|
| Execution Time: 0.187 ms
|
EXPLAIN ANALYZE
INSERT INTO "Event_Happening_Rating" (rating_id, event_happening_id, user_id, rating, comment)
SELECT COALESCE(MAX(rating_id), 0) + 1, 1, 1, 5, 'Test rating'
FROM "Event_Happening_Rating";
| QUERY PLAN
|
|---|
| Insert on "Event_Happening_Rating" (cost\=0.31..0.34 rows\=0 width\=0) (actual time\=0.521..0.522 rows\=0 loops\=1)
|
| -> Subquery Scan on "*SELECT*" (cost\=0.31..0.34 rows\=1 width\=60) (actual time\=0.122..0.123 rows\=1 loops\=1)
|
| -> Result (cost\=0.31..0.33 rows\=1 width\=52) (actual time\=0.119..0.120 rows\=1 loops\=1)
|
| InitPlan 1
|
| -> Limit (cost\=0.29..0.31 rows\=1 width\=8) (actual time\=0.114..0.115 rows\=1 loops\=1)
|
| -> Index Only Scan Backward using "Event_Happening_Rating_pkey" on "Event_Happening_Rating" "Event_Happening_Rating_1" (cost\=0.29..478.37 rows\=17872 width\=8) (actual time\=0.113..0.113 rows\=1 loops\=1)
|
| Heap Fetches: 6
|
| Planning Time: 0.297 ms
|
| Trigger for constraint fk_event_happening_rating_event_happening: time\=0.490 calls\=1
|
| Trigger for constraint fk_event_happening_rating_user: time\=0.370 calls\=1
|
| Execution Time: 1.444 ms
|
EXPLAIN ANALYZE
UPDATE "Event_Happening_Rating"
SET rating = 4, comment = 'New test rating'
WHERE event_happening_id = 1 AND user_id = 1;
| QUERY PLAN
|
|---|
| Update on "Event_Happening_Rating" (cost\=0.29..4.31 rows\=0 width\=0) (actual time\=0.176..0.177 rows\=0 loops\=1)
|
| -> Index Scan using idx_ehr_user_id on "Event_Happening_Rating" (cost\=0.29..4.31 rows\=1 width\=42) (actual time\=0.039..0.042 rows\=1 loops\=1)
|
| Index Cond: (user_id \= 1)
|
| Filter: (event_happening_id \= 1)
|
| Planning Time: 0.153 ms
|
| Execution Time: 0.218 ms
|
Без индекси, секое пребарување на оценките по корисник предизвикува непотребно оптоварување на меморијата преку Seq Scan. Индексирањето на user_id и event_happening_id обезбедува брза филтрација и поврзување на рејтинзите со соодветните термини на настаните.
Оптимизација:
-- index for linking ratings to event happenings
CREATE INDEX idx_ehr_happening_id ON "Event_Happening_Rating"(event_happening_id);
-- index for linking ratings to users
CREATE INDEX idx_ehr_user_id ON "Event_Happening_Rating"(user_id);
Со индекс:
EXPLAIN ANALYZE
SELECT * FROM "Event_User_Ratings" WHERE user_id = 1;
| QUERY PLAN
|
|---|
| Nested Loop (cost\=1.00..476.51 rows\=1 width\=131) (actual time\=359.514..359.524 rows\=1 loops\=1)
|
| -> Nested Loop (cost\=0.57..468.06 rows\=1 width\=113) (actual time\=359.389..359.396 rows\=1 loops\=1)
|
| -> Nested Loop (cost\=0.29..467.71 rows\=1 width\=82) (actual time\=2.104..2.110 rows\=1 loops\=1)
|
| -> Seq Scan on "Event_Happening_Rating" ehr (cost\=0.00..459.40 rows\=1 width\=74) (actual time\=2.038..2.041 rows\=1 loops\=1)
|
| Filter: (user_id \= 1)
|
| Rows Removed by Filter: 17871
|
| -> Index Scan using "Event_Happening_pkey" on "Event_Happening" eh (cost\=0.29..8.30 rows\=1 width\=16) (actual time\=0.061..0.062 rows\=1 loops\=1)
|
| Index Cond: (event_happening_id \= ehr.event_happening_id)
|
| -> Index Scan using "Event_pkey" on "Event" e (cost\=0.29..0.35 rows\=1 width\=39) (actual time\=357.279..357.280 rows\=1 loops\=1)
|
| Index Cond: (event_id \= eh.event_id)
|
| -> Index Scan using "User_pkey" on "User" u (cost\=0.43..8.45 rows\=1 width\=26) (actual time\=0.120..0.121 rows\=1 loops\=1)
|
| Index Cond: (user_id \= 1)
|
| Planning Time: 31.049 ms
|
| Execution Time: 359.600 ms
|
EXPLAIN ANALYZE
INSERT INTO "Event_Happening_Rating" (rating_id, event_happening_id, user_id, rating, comment)
SELECT COALESCE(MAX(rating_id), 0) + 1, 2, 1, 5, 'Test rating'
FROM "Event_Happening_Rating";
| QUERY PLAN
|
|---|
| Insert on "Event_Happening_Rating" (cost\=0.31..0.34 rows\=0 width\=0) (actual time\=0.482..0.483 rows\=0 loops\=1)
|
| -> Subquery Scan on "*SELECT*" (cost\=0.31..0.34 rows\=1 width\=60) (actual time\=0.117..0.119 rows\=1 loops\=1)
|
| -> Result (cost\=0.31..0.33 rows\=1 width\=52) (actual time\=0.115..0.115 rows\=1 loops\=1)
|
| InitPlan 1
|
| -> Limit (cost\=0.29..0.31 rows\=1 width\=8) (actual time\=0.110..0.111 rows\=1 loops\=1)
|
| -> Index Only Scan Backward using "Event_Happening_Rating_pkey" on "Event_Happening_Rating" "Event_Happening_Rating_1" (cost\=0.29..484.37 rows\=17872 width\=8) (actual time\=0.108..0.108 rows\=1 loops\=1)
|
| Heap Fetches: 4
|
| Planning Time: 0.203 ms
|
| Trigger for constraint fk_event_happening_rating_event_happening: time\=0.197 calls\=1
|
| Trigger for constraint fk_event_happening_rating_user: time\=0.114 calls\=1
|
| Execution Time: 0.842 ms
|
EXPLAIN ANALYZE
UPDATE "Event_Happening_Rating"
SET rating = 6, comment = 'New test rating'
WHERE event_happening_id = 1 AND user_id = 2;
| QUERY PLAN
|
|---|
| Update on "Event_Happening_Rating" (cost\=0.29..8.31 rows\=0 width\=0) (actual time\=0.021..0.022 rows\=0 loops\=1)
|
| -> Index Scan using uq_rating_happening_user on "Event_Happening_Rating" (cost\=0.29..8.31 rows\=1 width\=42) (actual time\=0.020..0.021 rows\=0 loops\=1)
|
| Index Cond: ((event_happening_id \= 1) AND (user_id \= 2))
|
| Planning Time: 0.132 ms
|
| Execution Time: 0.081 ms
|
Анализа и оптимизација на Event_Overall_Ratings
Овој поглед врши статистичка анализа на задоволството на публиката преку пресметување на просечната оцена и вкупниот број на рецензии за секој поединечен настан.
CREATE 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,
AVG(ehr.rating) AS average_rating
FROM "Event" e
JOIN "Event_Happening" eh ON e.event_id = eh.event_id
JOIN "Event_Happening_Rating" ehr ON eh.event_happening_id = ehr.event_happening_id
GROUP BY e.event_id, e.name, eh.event_happening_id, eh.event_time;
Без индекс:
EXPLAIN ANALYZE
SELECT * FROM "Event_Overall_Ratings" WHERE event_id = 1;
| QUERY PLAN
|
|---|
| GroupAggregate (cost\=72.25..72.32 rows\=3 width\=95) (actual time\=1029.642..1029.647 rows\=1 loops\=1)
|
| Group Key: eh.event_happening_id
|
| -> Sort (cost\=72.25..72.26 rows\=3 width\=67) (actual time\=1029.557..1029.604 rows\=1 loops\=1)
|
| Sort Key: eh.event_happening_id
|
| Sort Method: quicksort Memory: 25kB
|
| -> Nested Loop (cost\=4.90..72.22 rows\=3 width\=67) (actual time\=223.616..1029.548 rows\=1 loops\=1)
|
| -> Index Scan using "Event_pkey" on "Event" e (cost\=0.29..8.30 rows\=1 width\=39) (actual time\=0.046..0.053 rows\=1 loops\=1)
|
| Index Cond: (event_id \= 1)
|
| -> Nested Loop (cost\=4.61..63.89 rows\=3 width\=36) (actual time\=223.566..1029.488 rows\=1 loops\=1)
|
| -> Bitmap Heap Scan on "Event_Happening" eh (cost\=4.33..22.32 rows\=5 width\=24) (actual time\=0.054..805.890 rows\=5 loops\=1)
|
| Recheck Cond: (event_id \= 1)
|
| Heap Blocks: exact\=5
|
| -> Bitmap Index Scan on idx_event_happening_event_id (cost\=0.00..4.32 rows\=5 width\=0) (actual time\=0.029..0.029 rows\=5 loops\=1)
|
| Index Cond: (event_id \= 1)
|
| -> Index Scan using uq_rating_happening_user on "Event_Happening_Rating" ehr (cost\=0.29..8.30 rows\=1 width\=20) (actual time\=44.710..44.711 rows\=0 loops\=5)
|
| Index Cond: (event_happening_id \= eh.event_happening_id)
|
| Planning Time: 1.025 ms
|
| Execution Time: 1029.756 ms
|
EXPLAIN ANALYZE
INSERT INTO "Event_Happening_Rating" (rating_id, event_happening_id, user_id, rating, comment)
SELECT COALESCE(MAX(rating_id), 0) + 1, 1, 15, 9, 'Test rating'
FROM "Event_Happening_Rating";
| QUERY PLAN
|
|---|
| Insert on "Event_Happening_Rating" (cost\=0.31..0.34 rows\=0 width\=0) (actual time\=0.405..0.407 rows\=0 loops\=1)
|
| -> Subquery Scan on "*SELECT*" (cost\=0.31..0.34 rows\=1 width\=60) (actual time\=0.156..0.158 rows\=1 loops\=1)
|
| -> Result (cost\=0.31..0.33 rows\=1 width\=52) (actual time\=0.154..0.155 rows\=1 loops\=1)
|
| InitPlan 1
|
| -> Limit (cost\=0.29..0.31 rows\=1 width\=8) (actual time\=0.149..0.149 rows\=1 loops\=1)
|
| -> Index Only Scan Backward using "Event_Happening_Rating_pkey" on "Event_Happening_Rating" "Event_Happening_Rating_1" (cost\=0.29..484.37 rows\=17872 width\=8) (actual time\=0.148..0.148 rows\=1 loops\=1)
|
| Heap Fetches: 2
|
| Planning Time: 0.199 ms
|
| Trigger for constraint fk_event_happening_rating_event_happening: time\=0.232 calls\=1
|
| Trigger for constraint fk_event_happening_rating_user: time\=0.234 calls\=1
|
| Execution Time: 0.918 ms
|
EXPLAIN ANALYZE
UPDATE "Event_Happening_Rating"
SET rating = 8, comment = 'New test rating'
WHERE event_happening_id = 1 AND user_id = 15;
| QUERY PLAN
|
|---|
| Update on "Event_Happening_Rating" (cost\=0.29..8.31 rows\=0 width\=0) (actual time\=0.458..0.459 rows\=0 loops\=1)
|
| -> Index Scan using uq_rating_happening_user on "Event_Happening_Rating" (cost\=0.29..8.31 rows\=1 width\=42) (actual time\=0.131..0.133 rows\=1 loops\=1)
|
| Index Cond: ((event_happening_id \= 1) AND (user_id \= 15))
|
| Planning Time: 0.136 ms
|
| Execution Time: 0.500 ms
|
Пресметката на просечни оценки бара постојано агрегирање на податоци, што е бавно при секој нов приказ. Композитен индекс на (event_happening_id, rating) овозможува математичките операции да се вршат директно врз индексот, забрзувајќи го приказот на почетната страна.
Оптимизација:
-- composite index to speed up grouping and aggregate calculations (AVG, COUNT)
CREATE INDEX idx_ehr_happening_id_rating ON "Event_Happening_Rating"(event_happening_id, rating);
Со индекс:
EXPLAIN ANALYZE
SELECT * FROM "Event_Overall_Ratings" WHERE event_id = 1;
| QUERY PLAN
|
|---|
| GroupAggregate (cost\=72.25..72.32 rows\=3 width\=95) (actual time\=0.387..0.389 rows\=1 loops\=1)
|
| Group Key: eh.event_happening_id
|
| -> Sort (cost\=72.25..72.26 rows\=3 width\=67) (actual time\=0.374..0.375 rows\=1 loops\=1)
|
| Sort Key: eh.event_happening_id
|
| Sort Method: quicksort Memory: 25kB
|
| -> Nested Loop (cost\=4.90..72.22 rows\=3 width\=67) (actual time\=0.271..0.367 rows\=1 loops\=1)
|
| -> Index Scan using "Event_pkey" on "Event" e (cost\=0.29..8.30 rows\=1 width\=39) (actual time\=0.126..0.128 rows\=1 loops\=1)
|
| Index Cond: (event_id \= 1)
|
| -> Nested Loop (cost\=4.61..63.89 rows\=3 width\=36) (actual time\=0.142..0.235 rows\=1 loops\=1)
|
| -> Bitmap Heap Scan on "Event_Happening" eh (cost\=4.33..22.32 rows\=5 width\=24) (actual time\=0.067..0.144 rows\=5 loops\=1)
|
| Recheck Cond: (event_id \= 1)
|
| Heap Blocks: exact\=5
|
| -> Bitmap Index Scan on idx_event_happening_event_id (cost\=0.00..4.32 rows\=5 width\=0) (actual time\=0.038..0.038 rows\=5 loops\=1)
|
| Index Cond: (event_id \= 1)
|
| -> Index Scan using uq_rating_happening_user on "Event_Happening_Rating" ehr (cost\=0.29..8.30 rows\=1 width\=20) (actual time\=0.016..0.016 rows\=0 loops\=5)
|
| Index Cond: (event_happening_id \= eh.event_happening_id)
|
| Planning Time: 1.134 ms
|
| Execution Time: 0.459 ms
|
EXPLAIN ANALYZE
INSERT INTO "Event_Happening_Rating" (rating_id, event_happening_id, user_id, rating, comment)
SELECT COALESCE(MAX(rating_id), 0) + 1, 1, 20, 7, 'Test rating'
FROM "Event_Happening_Rating";
| QUERY PLAN
|
|---|
| Insert on "Event_Happening_Rating" (cost\=0.31..0.34 rows\=0 width\=0) (actual time\=0.538..0.539 rows\=0 loops\=1)
|
| -> Subquery Scan on "*SELECT*" (cost\=0.31..0.34 rows\=1 width\=60) (actual time\=0.111..0.112 rows\=1 loops\=1)
|
| -> Result (cost\=0.31..0.33 rows\=1 width\=52) (actual time\=0.109..0.109 rows\=1 loops\=1)
|
| InitPlan 1
|
| -> Limit (cost\=0.29..0.31 rows\=1 width\=8) (actual time\=0.104..0.105 rows\=1 loops\=1)
|
| -> Index Only Scan Backward using "Event_Happening_Rating_pkey" on "Event_Happening_Rating" "Event_Happening_Rating_1" (cost\=0.29..485.40 rows\=17874 width\=8) (actual time\=0.102..0.103 rows\=1 loops\=1)
|
| Heap Fetches: 1
|
| Planning Time: 0.195 ms
|
| Trigger for constraint fk_event_happening_rating_event_happening: time\=0.223 calls\=1
|
| Trigger for constraint fk_event_happening_rating_user: time\=0.172 calls\=1
|
| Execution Time: 0.980 ms
|
EXPLAIN ANALYZE
UPDATE "Event_Happening_Rating"
SET rating = 8, comment = 'New test rating'
WHERE event_happening_id = 1 AND user_id = 15;
| QUERY PLAN
|
|---|
| Update on "Event_Happening_Rating" (cost\=0.29..8.31 rows\=0 width\=0) (actual time\=0.482..0.483 rows\=0 loops\=1)
|
| -> Index Scan using uq_rating_happening_user on "Event_Happening_Rating" (cost\=0.29..8.31 rows\=1 width\=42) (actual time\=0.096..0.098 rows\=1 loops\=1)
|
| Index Cond: ((event_happening_id \= 1) AND (user_id \= 20))
|
| Planning Time: 0.163 ms
|
| Execution Time: 0.548 ms
|
Анализа и оптимизација на Event_Financial_Summary
Овој поглед ги сумира финансиските резултати за секој настан, прикажувајќи го вкупниот број на продадени билети, нето приходот по одбивање на рефундациите и посебно издвоената заработка од административните такси при враќање на влезниците.
CREATE VIEW "Event_Financial_Summary" AS
SELECT
e.event_id,
e.name AS event_name,
eh.event_happening_id,
eh.event_time,
COUNT(tp.purchase_id) AS total_tickets_sold,
-- total revenue
SUM(tp.purchase_amount) - SUM(CASE WHEN tr.refund_amount IS NOT NULL THEN tr.refund_amount ELSE 0 END) AS net_revenue,
-- refund taxes
SUM(CASE WHEN tr.refund_id IS NOT NULL THEN tp.purchase_amount - tr.refund_amount ELSE 0 END) AS refund_tax_profit
FROM "Event" e
JOIN "Event_Happening" eh ON e.event_id = eh.event_id
JOIN "Ticket" t ON eh.event_happening_id = t.event_happening_id
JOIN "Ticket_Purchase" tp ON t.ticket_id = tp.ticket_id
LEFT JOIN "Ticket_Refund" tr ON tp.purchase_id = tr.purchase_id
GROUP BY e.event_id, e.name, eh.event_happening_id, eh.event_time;
Без индекс:
EXPLAIN ANALYZE
SELECT * FROM "Event_Financial_Summary" WHERE event_id = 1;
| QUERY PLAN
|
|---|
| Finalize GroupAggregate (cost\=300491.02..300511.03 rows\=5 width\=71) (actual time\=319308.288..319318.376 rows\=1 loops\=1)
|
| Group Key: eh.event_happening_id
|
| -> Gather Merge (cost\=300491.02..300510.84 rows\=10 width\=75) (actual time\=319308.204..319318.298 rows\=1 loops\=1)
|
| Workers Planned: 2
|
| Workers Launched: 2
|
| -> Partial GroupAggregate (cost\=299491.00..299509.67 rows\=5 width\=75) (actual time\=319135.885..319135.894 rows\=0 loops\=3)
|
| Group Key: eh.event_happening_id
|
| -> Sort (cost\=299491.00..299493.66 rows\=1064 width\=79) (actual time\=319135.736..319135.767 rows\=388 loops\=3)
|
| Sort Key: eh.event_happening_id
|
| Sort Method: quicksort Memory: 25kB
|
| Worker 0: Sort Method: quicksort Memory: 25kB
|
| Worker 1: Sort Method: quicksort Memory: 160kB
|
| -> Nested Loop Left Join (cost\=17569.44..299437.50 rows\=1064 width\=79) (actual time\=257215.363..319135.341 rows\=388 loops\=3)
|
| -> Parallel Hash Join (cost\=17569.00..291013.94 rows\=1064 width\=67) (actual time\=257187.356..319052.018 rows\=388 loops\=3)
|
| Hash Cond: (tp.ticket_id \= t.ticket_id)
|
| -> Parallel Seq Scan on "Ticket_Purchase" tp (cost\=0.00..248438.67 rows\=6666668 width\=20) (actual time\=39.675..315154.157 rows\=5333334 loops\=3)
|
| -> Parallel Hash (cost\=17536.03..17536.03 rows\=2638 width\=63) (actual time\=2787.389..2787.393 rows\=1442 loops\=3)
|
| Buckets: 8192 Batches: 1 Memory Usage: 576kB
|
| -> Nested Loop (cost\=1.14..17536.03 rows\=2638 width\=63) (actual time\=1483.997..2465.151 rows\=1442 loops\=3)
|
| -> Nested Loop (cost\=0.57..1029.33 rows\=3 width\=55) (actual time\=632.603..753.210 rows\=2 loops\=3)
|
| -> Parallel Index Scan using "Event_Happening_pkey" on "Event_Happening" eh (cost\=0.29..1004.39 rows\=3 width\=24) (actual time\=632.410..752.992 rows\=2 loops\=3)
|
| Filter: (event_id \= 1)
|
| Rows Removed by Filter: 10444
|
| -> Index Scan using "Event_pkey" on "Event" e (cost\=0.29..8.30 rows\=1 width\=39) (actual time\=0.094..0.100 rows\=1 loops\=5)
|
| Index Cond: (event_id \= 1)
|
| -> Index Scan using uq_ticket_event_happening_seat on "Ticket" t (cost\=0.56..5486.81 rows\=1542 width\=16) (actual time\=652.636..1027.008 rows\=865 loops\=5)
|
| Index Cond: (event_happening_id \= eh.event_happening_id)
|
| -> Index Scan using idx_ticket_refund_purchase_id on "Ticket_Refund" tr (cost\=0.43..7.92 rows\=1 width\=20) (actual time\=0.214..0.214 rows\=0 loops\=1163)
|
| Index Cond: (purchase_id \= tp.purchase_id)
|
| Planning Time: 2770.897 ms
|
| JIT:
|
| Functions: 96
|
| Options: Inlining false, Optimization false, Expressions true, Deforming true
|
| Timing: Generation 9.017 ms (Deform 4.245 ms), Inlining 0.000 ms, Optimization 4.730 ms, Emission 79.393 ms, Total 93.140 ms
|
| Execution Time: 319320.792 ms
|
EXPLAIN ANALYZE
INSERT INTO "Ticket_Purchase" (purchase_id, ticket_id, user_id, qr_code, purchase_time, purchase_amount)
SELECT COALESCE(MAX(purchase_id), 0) + 1, 1, 1, 'QR-TEST-CODE-003', CURRENT_TIMESTAMP, 1500.00
FROM "Ticket_Purchase";
| QUERY PLAN
|
|---|
| Insert on "Ticket_Purchase" (cost\=0.70..0.73 rows\=0 width\=0) (actual time\=1391.459..1391.461 rows\=0 loops\=1)
|
| -> Subquery Scan on "*SELECT*" (cost\=0.70..0.73 rows\=1 width\=552) (actual time\=0.132..0.137 rows\=1 loops\=1)
|
| -> Result (cost\=0.70..0.71 rows\=1 width\=88) (actual time\=0.106..0.109 rows\=1 loops\=1)
|
| InitPlan 1
|
| -> Limit (cost\=0.43..0.70 rows\=1 width\=8) (actual time\=0.101..0.102 rows\=1 loops\=1)
|
| -> Index Only Scan Backward using "Ticket_Purchase_pkey" on "Ticket_Purchase" "Ticket_Purchase_1" (cost\=0.43..4162966.51 rows\=16000002 width\=8) (actual time\=0.099..0.099 rows\=1 loops\=1)
|
| Heap Fetches: 1
|
| Planning Time: 0.232 ms
|
| Trigger for constraint fk_purchase_ticket: time\=1.278 calls\=1
|
| Trigger for constraint fk_purchase_user: time\=0.358 calls\=1
|
| Execution Time: 1393.154 ms
|
EXPLAIN ANALYZE
UPDATE "Ticket_Purchase"
SET purchase_amount = 1800.00
WHERE qr_code = 'QR-TEST-CODE-003';
| QUERY PLAN
|
|---|
| Update on "Ticket_Purchase" (cost\=0.56..8.58 rows\=0 width\=0) (actual time\=0.266..0.267 rows\=0 loops\=1)
|
| -> Index Scan using "Ticket_Purchase_qr_code_key" on "Ticket_Purchase" (cost\=0.56..8.58 rows\=1 width\=10) (actual time\=0.117..0.119 rows\=1 loops\=1)
|
| Index Cond: ((qr_code)::text \= 'QR-TEST-CODE-003'::text)
|
| Planning Time: 0.188 ms
|
| Execution Time: 0.364 ms
|
Овој поглед има критично време на извршување од над 5 минути поради обработка на милиони трансакции. Индексите на ticket_id го намалуваат времето за 99%, овозможувајќи моментален преглед на приходите и рефундациите за секој настан.
Оптимизација:
-- index for linking ticket purchases to the specific tickets
CREATE INDEX idx_ticket_purchase_ticket_id ON "Ticket_Purchase"(ticket_id);
-- index for the LEFT JOIN with refunds to calculate net revenue accurately
CREATE INDEX idx_ticket_refund_purchase_id ON "Ticket_Refund"(purchase_id);
-- index for linking tickets to scheduled events
CREATE INDEX idx_ticket_event_happening_id ON "Ticket"(event_happening_id);
Со индекс:
EXPLAIN ANALYZE
SELECT * FROM "Event_Financial_Summary" WHERE event_id = 1;
| QUERY PLAN
|
|---|
| Finalize GroupAggregate (cost\=298955.06..298975.08 rows\=5 width\=71) (actual time\=1499.323..1508.016 rows\=1 loops\=1)
|
| Group Key: eh.event_happening_id
|
| -> Gather Merge (cost\=298955.06..298974.89 rows\=10 width\=75) (actual time\=1499.269..1507.962 rows\=1 loops\=1)
|
| Workers Planned: 2
|
| Workers Launched: 2
|
| -> Partial GroupAggregate (cost\=297955.04..297973.71 rows\=5 width\=75) (actual time\=1463.528..1463.535 rows\=0 loops\=3)
|
| Group Key: eh.event_happening_id
|
| -> Sort (cost\=297955.04..297957.70 rows\=1064 width\=79) (actual time\=1463.377..1463.407 rows\=388 loops\=3)
|
| Sort Key: eh.event_happening_id
|
| Sort Method: quicksort Memory: 25kB
|
| Worker 0: Sort Method: quicksort Memory: 25kB
|
| Worker 1: Sort Method: quicksort Memory: 160kB
|
| -> Nested Loop Left Join (cost\=16033.48..297901.55 rows\=1064 width\=79) (actual time\=1201.975..1463.060 rows\=388 loops\=3)
|
| -> Parallel Hash Join (cost\=16033.05..289477.99 rows\=1064 width\=67) (actual time\=1201.913..1460.100 rows\=388 loops\=3)
|
| Hash Cond: (tp.ticket_id \= t.ticket_id)
|
| -> Parallel Seq Scan on "Ticket_Purchase" tp (cost\=0.00..248438.67 rows\=6666668 width\=20) (actual time\=0.091..663.287 rows\=5333334 loops\=3)
|
| -> Parallel Hash (cost\=16000.08..16000.08 rows\=2638 width\=63) (actual time\=13.806..13.809 rows\=1442 loops\=3)
|
| Buckets: 8192 Batches: 1 Memory Usage: 512kB
|
| -> Nested Loop (cost\=1.01..16000.08 rows\=2638 width\=63) (actual time\=30.720..39.526 rows\=4325 loops\=1)
|
| -> Nested Loop (cost\=0.57..1029.33 rows\=3 width\=55) (actual time\=30.641..37.392 rows\=5 loops\=1)
|
| -> Parallel Index Scan using "Event_Happening_pkey" on "Event_Happening" eh (cost\=0.29..1004.39 rows\=3 width\=24) (actual time\=30.536..37.242 rows\=5 loops\=1)
|
| Filter: (event_id \= 1)
|
| Rows Removed by Filter: 31332
|
| -> Index Scan using "Event_pkey" on "Event" e (cost\=0.29..8.30 rows\=1 width\=39) (actual time\=0.021..0.023 rows\=1 loops\=5)
|
| Index Cond: (event_id \= 1)
|
| -> Index Scan using idx_ticket_event_happening_id on "Ticket" t (cost\=0.44..4974.83 rows\=1542 width\=16) (actual time\=0.052..0.324 rows\=865 loops\=5)
|
| Index Cond: (event_happening_id \= eh.event_happening_id)
|
| -> Index Scan using idx_ticket_refund_purchase_id on "Ticket_Refund" tr (cost\=0.43..7.92 rows\=1 width\=20) (actual time\=0.007..0.007 rows\=0 loops\=1163)
|
| Index Cond: (purchase_id \= tp.purchase_id)
|
| Planning Time: 1.604 ms
|
| JIT:
|
| Functions: 96
|
| Options: Inlining false, Optimization false, Expressions true, Deforming true
|
| Timing: Generation 8.629 ms (Deform 3.959 ms), Inlining 0.000 ms, Optimization 3.603 ms, Emission 79.354 ms, Total 91.585 ms
|
| Execution Time: 1510.555 ms
|
EXPLAIN ANALYZE
INSERT INTO "Ticket_Purchase" (purchase_id, ticket_id, user_id, qr_code, purchase_time, purchase_amount)
SELECT COALESCE(MAX(purchase_id), 0) + 1, 1, 2, 'QR-TEST-CODE-004', CURRENT_TIMESTAMP, 1400.00
FROM "Ticket_Purchase";
| QUERY PLAN
|
|---|
| Insert on "Ticket_Purchase" (cost\=0.70..0.73 rows\=0 width\=0) (actual time\=0.351..0.352 rows\=0 loops\=1)
|
| -> Subquery Scan on "*SELECT*" (cost\=0.70..0.73 rows\=1 width\=552) (actual time\=0.145..0.147 rows\=1 loops\=1)
|
| -> Result (cost\=0.70..0.71 rows\=1 width\=88) (actual time\=0.132..0.133 rows\=1 loops\=1)
|
| InitPlan 1
|
| -> Limit (cost\=0.43..0.70 rows\=1 width\=8) (actual time\=0.126..0.127 rows\=1 loops\=1)
|
| -> Index Only Scan Backward using "Ticket_Purchase_pkey" on "Ticket_Purchase" "Ticket_Purchase_1" (cost\=0.43..4162966.51 rows\=16000002 width\=8) (actual time\=0.124..0.124 rows\=1 loops\=1)
|
| Heap Fetches: 2
|
| Planning Time: 0.208 ms
|
| Trigger for constraint fk_purchase_ticket: time\=0.225 calls\=1
|
| Trigger for constraint fk_purchase_user: time\=0.176 calls\=1
|
| Execution Time: 0.802 ms
|
EXPLAIN ANALYZE
UPDATE "Ticket_Purchase"
SET purchase_amount = 1700.00
WHERE qr_code = 'QR-TEST-CODE-004';
| QUERY PLAN
|
|---|
| Update on "Ticket_Purchase" (cost\=0.56..8.58 rows\=0 width\=0) (actual time\=0.161..0.162 rows\=0 loops\=1)
|
| -> Index Scan using "Ticket_Purchase_qr_code_key" on "Ticket_Purchase" (cost\=0.56..8.58 rows\=1 width\=10) (actual time\=0.115..0.116 rows\=1 loops\=1)
|
| Index Cond: ((qr_code)::text \= 'QR-TEST-CODE-004'::text)
|
| Planning Time: 0.134 ms
|
| Execution Time: 0.195 ms
|
Анализа и оптимизација на Future_Events
Овој поглед служи за динамично генерирање на репертоарот, прикажувајќи ги исклучиво претстојните настани во реално време преку филтрирање на изминатите термини.
CREATE 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_city AS city
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;
Без индекс:
EXPLAIN ANALYZE
SELECT * FROM "Future_Events"
WHERE venue_id = 1;
| QUERY PLAN
|
|---|
| Nested Loop (cost\=0.86..26.26 rows\=1 width\=92) (actual time\=0.055..0.056 rows\=0 loops\=1)
|
| -> Nested Loop (cost\=0.57..17.94 rows\=1 width\=63) (actual time\=0.054..0.055 rows\=0 loops\=1)
|
| -> Index Scan using uq_happening_time_venue on "Event_Happening" eh (cost\=0.29..9.64 rows\=1 width\=32) (actual time\=0.054..0.054 rows\=0 loops\=1)
|
| Index Cond: ((event_time > CURRENT_TIMESTAMP) AND (venue_id \= 1))
|
| -> Index Scan using "Event_pkey" on "Event" e (cost\=0.29..8.30 rows\=1 width\=39) (never executed)
|
| Index Cond: (event_id \= eh.event_id)
|
| -> Index Scan using "Venue_pkey" on "Venue" v (cost\=0.29..8.30 rows\=1 width\=37) (never executed)
|
| Index Cond: (venue_id \= 1)
|
| Planning Time: 4.258 ms
|
| Execution Time: 0.193 ms
|
EXPLAIN ANALYZE
INSERT INTO "Event_Happening" (event_happening_id, event_id, venue_id, event_time, duration)
VALUES ((SELECT MAX(event_happening_id) + 1 FROM "Event_Happening"), 1, 1, '2026-12-31 20:00:00', 120);
| QUERY PLAN
|
|---|
| Insert on "Event_Happening" (cost\=0.33..0.34 rows\=0 width\=0) (actual time\=572.079..572.083 rows\=0 loops\=1)
|
| InitPlan 2
|
| -> Result (cost\=0.31..0.33 rows\=1 width\=8) (actual time\=0.260..0.262 rows\=1 loops\=1)
|
| InitPlan 1
|
| -> Limit (cost\=0.29..0.31 rows\=1 width\=8) (actual time\=0.256..0.258 rows\=1 loops\=1)
|
| -> Index Only Scan Backward using "Event_Happening_pkey" on "Event_Happening" "Event_Happening_1" (cost\=0.29..822.34 rows\=31337 width\=8) (actual time\=0.255..0.256 rows\=1 loops\=1)
|
| Heap Fetches: 0
|
| -> Result (cost\=0.00..0.01 rows\=1 width\=36) (actual time\=0.263..0.264 rows\=1 loops\=1)
|
| Planning Time: 0.199 ms
|
| Trigger for constraint fk_event_happening_event: time\=1.080 calls\=1
|
| Trigger for constraint fk_event_happening_venue: time\=0.448 calls\=1
|
| Execution Time: 573.660 ms
|
EXPLAIN ANALYZE
UPDATE "Event_Happening"
SET event_time = '2027-01-01 21:00:00'
WHERE event_happening_id = (SELECT MAX(event_happening_id) FROM "Event_Happening");
| QUERY PLAN
|
|---|
| Update on "Event_Happening" (cost\=0.61..8.63 rows\=0 width\=0) (actual time\=0.569..0.570 rows\=0 loops\=1)
|
| InitPlan 2
|
| -> Result (cost\=0.31..0.32 rows\=1 width\=8) (actual time\=0.292..0.293 rows\=1 loops\=1)
|
| InitPlan 1
|
| -> Limit (cost\=0.29..0.31 rows\=1 width\=8) (actual time\=0.288..0.288 rows\=1 loops\=1)
|
| -> Index Only Scan Backward using "Event_Happening_pkey" on "Event_Happening" "Event_Happening_1" (cost\=0.29..822.34 rows\=31337 width\=8) (actual time\=0.287..0.287 rows\=1 loops\=1)
|
| Heap Fetches: 1
|
| -> Index Scan using "Event_Happening_pkey" on "Event_Happening" (cost\=0.29..8.30 rows\=1 width\=14) (actual time\=0.310..0.312 rows\=1 loops\=1)
|
| Index Cond: (event_happening_id \= (InitPlan 2).col1)
|
| Planning Time: 0.240 ms
|
| Execution Time: 0.662 ms
|
Времето на извршување е релативно ниско, но базата троши 4.258 ms само на планирање на секој поединечен запис. Бидејќи редовите не се подредени по време, системот мора да врши постојани споредби за секој настан. Заради ова, потребен е индекс кој ќе овозможи моментално лоцирање на идните настани без пребарување на целата табела.
Оптимизација:
-- index for optimizing chronological filtering of future events
CREATE INDEX idx_event_happening_time ON "Event_Happening"(event_time);
-- index for accelerating lookups of events scheduled at specific venues
CREATE INDEX idx_event_happening_venue_id ON "Event_Happening"(venue_id);
-- composite index to optimize queries filtering both by venue and upcoming time slots
CREATE INDEX idx_event_happening_venue_time ON "Event_Happening"(venue_id, event_time);
Со индекс:
EXPLAIN ANALYZE
SELECT * FROM "Future_Events"
WHERE venue_id = 1;
| QUERY PLAN
|
|---|
| Nested Loop (cost\=0.86..24.93 rows\=1 width\=92) (actual time\=0.153..0.157 rows\=1 loops\=1)
|
| -> Nested Loop (cost\=0.57..16.61 rows\=1 width\=63) (actual time\=0.081..0.083 rows\=1 loops\=1)
|
| -> Index Scan using idx_event_happening_venue_time on "Event_Happening" eh (cost\=0.29..8.31 rows\=1 width\=32) (actual time\=0.067..0.068 rows\=1 loops\=1)
|
| Index Cond: ((venue_id \= 1) AND (event_time > CURRENT_TIMESTAMP))
|
| -> Index Scan using "Event_pkey" on "Event" e (cost\=0.29..8.30 rows\=1 width\=39) (actual time\=0.009..0.009 rows\=1 loops\=1)
|
| Index Cond: (event_id \= eh.event_id)
|
| -> Index Scan using "Venue_pkey" on "Venue" v (cost\=0.29..8.30 rows\=1 width\=37) (actual time\=0.070..0.071 rows\=1 loops\=1)
|
| Index Cond: (venue_id \= 1)
|
| Planning Time: 203.237 ms
|
| Execution Time: 0.215 ms
|
EXPLAIN ANALYZE
INSERT INTO "Event_Happening" (event_happening_id, event_id, venue_id, event_time, duration)
VALUES ((SELECT MAX(event_happening_id) + 1 FROM "Event_Happening"), 1, 1, '2026-11-15 19:00:00', 90);
| QUERY PLAN
|
|---|
| Insert on "Event_Happening" (cost\=0.33..0.34 rows\=0 width\=0) (actual time\=0.726..0.728 rows\=0 loops\=1)
|
| InitPlan 2
|
| -> Result (cost\=0.31..0.33 rows\=1 width\=8) (actual time\=0.281..0.282 rows\=1 loops\=1)
|
| InitPlan 1
|
| -> Limit (cost\=0.29..0.31 rows\=1 width\=8) (actual time\=0.278..0.279 rows\=1 loops\=1)
|
| -> Index Only Scan Backward using "Event_Happening_pkey" on "Event_Happening" "Event_Happening_1" (cost\=0.29..826.36 rows\=31338 width\=8) (actual time\=0.276..0.277 rows\=1 loops\=1)
|
| Heap Fetches: 1
|
| -> Result (cost\=0.00..0.01 rows\=1 width\=36) (actual time\=0.284..0.284 rows\=1 loops\=1)
|
| Planning Time: 0.235 ms
|
| Trigger for constraint fk_event_happening_event: time\=0.252 calls\=1
|
| Trigger for constraint fk_event_happening_venue: time\=0.255 calls\=1
|
| Execution Time: 1.278 ms
|
EXPLAIN ANALYZE
UPDATE "Event_Happening"
SET event_time = '2026-11-20 20:00:00'
WHERE event_happening_id = (SELECT MAX(event_happening_id) FROM "Event_Happening");
| QUERY PLAN
|
|---|
| Update on "Event_Happening" (cost\=0.61..8.63 rows\=0 width\=0) (actual time\=0.543..0.544 rows\=0 loops\=1)
|
| InitPlan 2
|
| -> Result (cost\=0.31..0.32 rows\=1 width\=8) (actual time\=0.169..0.170 rows\=1 loops\=1)
|
| InitPlan 1
|
| -> Limit (cost\=0.29..0.31 rows\=1 width\=8) (actual time\=0.166..0.166 rows\=1 loops\=1)
|
| -> Index Only Scan Backward using "Event_Happening_pkey" on "Event_Happening" "Event_Happening_1" (cost\=0.29..826.36 rows\=31338 width\=8) (actual time\=0.165..0.165 rows\=1 loops\=1)
|
| Heap Fetches: 1
|
| -> Index Scan using "Event_Happening_pkey" on "Event_Happening" (cost\=0.29..8.30 rows\=1 width\=14) (actual time\=0.183..0.185 rows\=1 loops\=1)
|
| Index Cond: (event_happening_id \= (InitPlan 2).col1)
|
| Planning Time: 0.283 ms
|
| Execution Time: 0.616 ms
|
Анализа и оптимизација на Available_Tickets
Овој поглед овозможува моментален увид во инвентарот на достапни седишта и автоматско враќање на рефундираните билети во понуда.
CREATE VIEW "Available_Tickets" AS
SELECT
t.ticket_id,
t.ticket_type,
t.base_price,
e.event_id,
e.name AS event_name,
eh.event_happening_id,
eh.event_time,
s.name AS section_name,
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 "Seat" st ON t.seat_id = st.seat_id
JOIN "Section" s ON st.section_id = s.section_id
WHERE t.is_available = TRUE;
Без индекс:
EXPLAIN ANALYZE
SELECT * FROM "Available_Tickets"
WHERE event_id = 1;
| QUERY PLAN
|
|---|
| Merge Join (cost\=27256.32..27455.75 rows\=811 width\=85) (actual time\=299.567..948.130 rows\=347 loops\=1)
|
| Merge Cond: (st.seat_id \= t.seat_id)
|
| -> Nested Loop (cost\=0.86..2908890.51 rows\=20753360 width\=19) (actual time\=19.526..667.820 rows\=1163 loops\=1)
|
| -> Index Scan using "Seat_pkey" on "Seat" st (cost\=0.56..2383051.22 rows\=20753360 width\=20) (actual time\=0.716..648.333 rows\=1163 loops\=1)
|
| -> Memoize (cost\=0.30..0.32 rows\=1 width\=15) (actual time\=0.016..0.016 rows\=1 loops\=1163)
|
| Cache Key: st.section_id
|
| Cache Mode: logical
|
| Hits: 1156 Misses: 7 Evictions: 0 Overflows: 0 Memory Usage: 1kB
|
| -> Index Scan using "Section_pkey" on "Section" s (cost\=0.29..0.31 rows\=1 width\=15) (actual time\=2.600..2.600 rows\=1 loops\=7)
|
| Index Cond: (section_id \= st.section_id)
|
| -> Sort (cost\=27253.12..27255.15 rows\=811 width\=82) (actual time\=280.018..280.056 rows\=347 loops\=1)
|
| Sort Key: t.seat_id
|
| Sort Method: quicksort Memory: 62kB
|
| -> Nested Loop (cost\=5.05..27213.93 rows\=811 width\=82) (actual time\=0.827..279.855 rows\=347 loops\=1)
|
| -> Nested Loop (cost\=4.61..30.67 rows\=5 width\=55) (actual time\=0.243..0.408 rows\=7 loops\=1)
|
| -> Index Scan using "Event_pkey" on "Event" e (cost\=0.29..8.30 rows\=1 width\=39) (actual time\=0.141..0.153 rows\=1 loops\=1)
|
| Index Cond: (event_id \= 1)
|
| -> Bitmap Heap Scan on "Event_Happening" eh (cost\=4.33..22.32 rows\=5 width\=24) (actual time\=0.095..0.239 rows\=7 loops\=1)
|
| Recheck Cond: (event_id \= 1)
|
| Heap Blocks: exact\=6
|
| -> Bitmap Index Scan on idx_event_happening_event_id (cost\=0.00..4.32 rows\=5 width\=0) (actual time\=0.066..0.066 rows\=11 loops\=1)
|
| Index Cond: (event_id \= 1)
|
| -> Index Scan using idx_ticket_event_happening_id on "Ticket" t (cost\=0.44..5433.49 rows\=316 width\=35) (actual time\=39.884..39.910 rows\=50 loops\=7)
|
| Index Cond: (event_happening_id \= eh.event_happening_id)
|
| Filter: is_available
|
| Rows Removed by Filter: 568
|
| Planning Time: 3.990 ms
|
| Execution Time: 948.253 ms
|
EXPLAIN ANALYZE
INSERT INTO "Ticket" (ticket_id, ticket_type, base_price, is_available, event_happening_id, seat_id)
VALUES ((SELECT MAX(ticket_id) + 1 FROM "Ticket"), 'Standard', 500.00, TRUE, 1, 999999);
| QUERY PLAN
|
|---|
| Insert on "Ticket" (cost\=0.61..0.62 rows\=0 width\=0) (actual time\=0.429..0.430 rows\=0 loops\=1)
|
| InitPlan 2
|
| -> Result (cost\=0.59..0.61 rows\=1 width\=8) (actual time\=0.215..0.216 rows\=1 loops\=1)
|
| InitPlan 1
|
| -> Limit (cost\=0.56..0.59 rows\=1 width\=8) (actual time\=0.212..0.213 rows\=1 loops\=1)
|
| -> Index Only Scan Backward using "Ticket_pkey" on "Ticket" "Ticket_1" (cost\=0.56..892367.26 rows\=28748980 width\=8) (actual time\=0.211..0.212 rows\=1 loops\=1)
|
| Heap Fetches: 1
|
| -> Result (cost\=0.00..0.01 rows\=1 width\=147) (actual time\=0.218..0.218 rows\=1 loops\=1)
|
| Planning Time: 0.211 ms
|
| Trigger for constraint fk_ticket_event_happening: time\=0.325 calls\=1
|
| Trigger for constraint fk_ticket_seat: time\=0.243 calls\=1
|
| Execution Time: 1.045 ms
|
EXPLAIN ANALYZE
UPDATE "Ticket"
SET base_price = 600.00
WHERE ticket_id = (SELECT MAX(ticket_id) FROM "Ticket");
| QUERY PLAN
|
|---|
| Update on "Ticket" (cost\=1.17..9.18 rows\=0 width\=0) (actual time\=0.340..0.341 rows\=0 loops\=1)
|
| InitPlan 2
|
| -> Result (cost\=0.59..0.60 rows\=1 width\=8) (actual time\=0.270..0.271 rows\=1 loops\=1)
|
| InitPlan 1
|
| -> Limit (cost\=0.56..0.59 rows\=1 width\=8) (actual time\=0.267..0.268 rows\=1 loops\=1)
|
| -> Index Only Scan Backward using "Ticket_pkey" on "Ticket" "Ticket_1" (cost\=0.56..892367.26 rows\=28748980 width\=8) (actual time\=0.266..0.266 rows\=1 loops\=1)
|
| Heap Fetches: 1
|
| -> Index Scan using "Ticket_pkey" on "Ticket" (cost\=0.56..8.58 rows\=1 width\=10) (actual time\=0.291..0.292 rows\=1 loops\=1)
|
| Index Cond: (ticket_id \= (InitPlan 2).col1)
|
| Planning Time: 0.244 ms
|
| Execution Time: 0.393 ms
|
За овој поглед се трошат 948.13 ms бидејќи базата мора да пребарува низ 30 милиони записи. Овој процес вклучува скапи операции со трошок од 27455.75, каде се читаат милиони непотребни записи од дискот. Заради ова, потребен е индекс кој ќе ги издвои само достапните билети и ќе го елиминира ваквото чекање.
Оптимизација:
-- partial index for rapid retrieval of only unsold tickets by filtering out sold ones
CREATE INDEX idx_ticket_is_available_true ON "Ticket"(is_available) WHERE is_available = TRUE;
-- index for linking tickets to scheduled events to accelerate event-based filtering
CREATE INDEX idx_ticket_event_happening_id ON "Ticket"(event_happening_id);
-- index for optimizing seat-based joins to retrieve venue layout details for each ticket
CREATE INDEX idx_ticket_seat_id ON "Ticket"(seat_id);
Со индекс:
EXPLAIN ANALYZE
SELECT * FROM "Available_Tickets"
WHERE event_id = 1;
| QUERY PLAN
|
|---|
| Merge Join (cost\=26670.90..26870.11 rows\=793 width\=85) (actual time\=2.496..3.734 rows\=347 loops\=1)
|
| Merge Cond: (st.seat_id \= t.seat_id)
|
| -> Nested Loop (cost\=0.86..2908890.51 rows\=20753360 width\=19) (actual time\=0.270..1.289 rows\=1163 loops\=1)
|
| -> Index Scan using "Seat_pkey" on "Seat" st (cost\=0.56..2383051.22 rows\=20753360 width\=20) (actual time\=0.049..0.516 rows\=1163 loops\=1)
|
| -> Memoize (cost\=0.30..0.32 rows\=1 width\=15) (actual time\=0.000..0.000 rows\=1 loops\=1163)
|
| Cache Key: st.section_id
|
| Cache Mode: logical
|
| Hits: 1156 Misses: 7 Evictions: 0 Overflows: 0 Memory Usage: 1kB
|
| -> Index Scan using "Section_pkey" on "Section" s (cost\=0.29..0.31 rows\=1 width\=15) (actual time\=0.009..0.009 rows\=1 loops\=7)
|
| Index Cond: (section_id \= st.section_id)
|
| -> Sort (cost\=26667.76..26669.74 rows\=793 width\=82) (actual time\=2.213..2.236 rows\=347 loops\=1)
|
| Sort Key: t.seat_id
|
| Sort Method: quicksort Memory: 62kB
|
| -> Nested Loop (cost\=5.05..26629.57 rows\=793 width\=82) (actual time\=0.566..2.095 rows\=347 loops\=1)
|
| -> Nested Loop (cost\=4.61..30.67 rows\=5 width\=55) (actual time\=0.153..0.260 rows\=7 loops\=1)
|
| -> Index Scan using "Event_pkey" on "Event" e (cost\=0.29..8.30 rows\=1 width\=39) (actual time\=0.077..0.083 rows\=1 loops\=1)
|
| Index Cond: (event_id \= 1)
|
| -> Bitmap Heap Scan on "Event_Happening" eh (cost\=4.33..22.32 rows\=5 width\=24) (actual time\=0.072..0.168 rows\=7 loops\=1)
|
| Recheck Cond: (event_id \= 1)
|
| Heap Blocks: exact\=6
|
| -> Bitmap Index Scan on idx_event_happening_event_id (cost\=0.00..4.32 rows\=5 width\=0) (actual time\=0.047..0.047 rows\=11 loops\=1)
|
| Index Cond: (event_id \= 1)
|
| -> Index Scan using idx_ticket_event_happening_id on "Ticket" t (cost\=0.44..5316.69 rows\=309 width\=35) (actual time\=0.234..0.253 rows\=50 loops\=7)
|
| Index Cond: (event_happening_id \= eh.event_happening_id)
|
| Filter: is_available
|
| Rows Removed by Filter: 568
|
| Planning Time: 4.840 ms
|
| Execution Time: 3.882 ms
|
EXPLAIN ANALYZE
INSERT INTO "Ticket" (ticket_id, ticket_type, base_price, is_available, event_happening_id, seat_id)
VALUES ((SELECT MAX(ticket_id) + 1 FROM "Ticket"), 'Standard', 500.00, TRUE, 1, 888888);
| QUERY PLAN
|
|---|
| Insert on "Ticket" (cost\=0.61..0.62 rows\=0 width\=0) (actual time\=0.545..0.546 rows\=0 loops\=1)
|
| InitPlan 2
|
| -> Result (cost\=0.59..0.61 rows\=1 width\=8) (actual time\=0.092..0.093 rows\=1 loops\=1)
|
| InitPlan 1
|
| -> Limit (cost\=0.56..0.59 rows\=1 width\=8) (actual time\=0.089..0.090 rows\=1 loops\=1)
|
| -> Index Only Scan Backward using "Ticket_pkey" on "Ticket" "Ticket_1" (cost\=0.56..882762.56 rows\=28108400 width\=8) (actual time\=0.088..0.089 rows\=1 loops\=1)
|
| Heap Fetches: 1
|
| -> Result (cost\=0.00..0.01 rows\=1 width\=147) (actual time\=0.095..0.095 rows\=1 loops\=1)
|
| Planning Time: 0.250 ms
|
| Trigger for constraint fk_ticket_event_happening: time\=0.473 calls\=1
|
| Trigger for constraint fk_ticket_seat: time\=207.063 calls\=1
|
| Execution Time: 208.133 ms
|
EXPLAIN ANALYZE
UPDATE "Ticket"
SET base_price = 700.00
WHERE ticket_id = (SELECT MAX(ticket_id) FROM "Ticket");
| QUERY PLAN
|
|---|
| Update on "Ticket" (cost\=1.17..9.18 rows\=0 width\=0) (actual time\=0.168..0.169 rows\=0 loops\=1)
|
| InitPlan 2
|
| -> Result (cost\=0.59..0.60 rows\=1 width\=8) (actual time\=0.033..0.034 rows\=1 loops\=1)
|
| InitPlan 1
|
| -> Limit (cost\=0.56..0.59 rows\=1 width\=8) (actual time\=0.031..0.031 rows\=1 loops\=1)
|
| -> Index Only Scan Backward using "Ticket_pkey" on "Ticket" "Ticket_1" (cost\=0.56..882762.56 rows\=28108400 width\=8) (actual time\=0.030..0.030 rows\=1 loops\=1)
|
| Heap Fetches: 1
|
| -> Index Scan using "Ticket_pkey" on "Ticket" (cost\=0.56..8.58 rows\=1 width\=10) (actual time\=0.045..0.046 rows\=1 loops\=1)
|
| Index Cond: (ticket_id \= (InitPlan 2).col1)
|
| Planning Time: 0.242 ms
|
| Execution Time: 0.397 ms
|