CinemaDB/Faza4: CinemaDB_Faza4.sql

File CinemaDB_Faza4.sql, 25.2 KB (added by 233280, 18 hours ago)
Line 
1CREATE OR REPLACE PROCEDURE sp_buy_ticket(
2 p_user_id INT,
3 p_showtime_id INT,
4 p_seat_id INT,
5 p_payment_method VARCHAR(50),
6 p_employee_id INT DEFAULT 1
7)
8LANGUAGE plpgsql
9AS $$
10DECLARE
11 v_reservation_id INT;
12 v_ticket_id INT;
13 v_price INT;
14 v_seat_type_id INT;
15 v_base_price INT;
16 v_start_time TIMESTAMP;
17 v_hall_id INT;
18 v_movie_title VARCHAR;
19 v_seat_row INT;
20 v_seat_number INT;
21 v_seat_type VARCHAR;
22BEGIN
23 SELECT s.start_time, s.base_price, s.hall_id, m.title
24 INTO v_start_time, v_base_price, v_hall_id, v_movie_title
25 FROM Showtime s
26 JOIN Movie m ON m.movie_id = s.movie_id
27 WHERE s.showtime_id = p_showtime_id;
28
29 IF NOT FOUND THEN
30 RAISE EXCEPTION 'Проекцијата не постои.';
31 END IF;
32
33 IF v_start_time < CURRENT_TIMESTAMP THEN
34 RAISE EXCEPTION 'Не можете да купите билет за завршена проекција.';
35 END IF;
36
37 SELECT se.seat_type_id, se.seat_row, se.seat_number, st.type
38 INTO v_seat_type_id, v_seat_row, v_seat_number, v_seat_type
39 FROM Seat se
40 JOIN Seat_Type st ON st.seat_type_id = se.seat_type_id
41 WHERE se.seat_id = p_seat_id
42 AND se.hall_id = v_hall_id;
43
44 IF NOT FOUND THEN
45 RAISE EXCEPTION 'Седиштето не постои или не припаѓа на салата за оваа проекција.';
46 END IF;
47
48 IF EXISTS (
49 SELECT 1 FROM Ticket
50 WHERE showtime_id = p_showtime_id
51 AND seat_id = p_seat_id
52 ) THEN
53 RAISE EXCEPTION 'Седиштето Ред % Број % е веќе зафатено.', v_seat_row, v_seat_number;
54 END IF;
55
56 v_price := (v_base_price * CASE v_seat_type_id
57 WHEN 1 THEN 1.0
58 WHEN 2 THEN 2.0
59 WHEN 3 THEN 1.5
60 WHEN 4 THEN 1.8
61 WHEN 5 THEN 0.7
62 ELSE 1.0
63 END)::INT;
64
65
66 INSERT INTO Reservation (user_id, showtime_id, employee_id, reservation_date, status)
67 VALUES (p_user_id, p_showtime_id, p_employee_id, CURRENT_DATE, 'CONFIRMED')
68 RETURNING reservation_id INTO v_reservation_id;
69
70
71 INSERT INTO Ticket (showtime_id, seat_id, reservation_id, price, purchase_date)
72 VALUES (p_showtime_id, p_seat_id, v_reservation_id, v_price, CURRENT_DATE)
73 RETURNING ticket_id INTO v_ticket_id;
74
75 INSERT INTO ReservationPayment (user_id, reservation_id, employee_id, amount, payment_date, payment_method)
76 VALUES (p_user_id, v_reservation_id, p_employee_id, v_price, CURRENT_DATE, p_payment_method);
77
78 RAISE NOTICE '=================================';
79 RAISE NOTICE 'Билетот е успешно купен!';
80 RAISE NOTICE 'Филм: %', v_movie_title;
81 RAISE NOTICE 'Седиште: Ред % - Број % (%)', v_seat_row, v_seat_number, v_seat_type;
82 RAISE NOTICE 'Цена: % ден', v_price;
83 RAISE NOTICE 'Начин: %', p_payment_method;
84 RAISE NOTICE 'Билет ID: %', v_ticket_id;
85 RAISE NOTICE 'Резерв ID: %', v_reservation_id;
86 RAISE NOTICE '=================================';
87
88EXCEPTION
89 WHEN OTHERS THEN
90 RAISE EXCEPTION 'Грешка при купување билет: %', SQLERRM;
91END;
92$$;
93
94SELECT
95 s.showtime_id,
96 m.title,
97 s.start_time,
98 s.hall_id,
99 s.base_price
100FROM Showtime s
101JOIN Movie m ON m.movie_id = s.movie_id
102WHERE s.start_time > CURRENT_TIMESTAMP
103LIMIT 5;
104
105SELECT se.seat_id, se.seat_row, se.seat_number, st.type, st.price
106FROM Seat se
107JOIN Seat_Type st ON st.seat_type_id = se.seat_type_id
108WHERE se.hall_id = 1
109 AND se.seat_id NOT IN (
110 SELECT seat_id FROM Ticket WHERE showtime_id = 1761211
111 )
112LIMIT 5;
113CALL sp_buy_ticket(
114 1,
115 1761211,
116 1,
117 'Credit Card'
118);
119
120CALL sp_buy_ticket(2, 1761211, 1, 'Cash');
121
122CALL sp_buy_ticket(1, 1761211, 2, 'Online');
123
124CALL sp_buy_ticket(1, 1761211, 5, 'Cash');
125
126
127
128
129CREATE OR REPLACE PROCEDURE sp_place_order(
130 p_user_id INT,
131 p_product_id INT,
132 p_quantity INT,
133 p_employee_id INT DEFAULT 1
134)
135LANGUAGE plpgsql
136AS $$
137DECLARE
138 v_stock INT;
139 v_price INT;
140 v_order_id INT;
141BEGIN
142
143 SELECT stock_quantity, price INTO v_stock, v_price
144 FROM Product WHERE product_id = p_product_id;
145
146 IF NOT FOUND THEN
147 RAISE EXCEPTION 'Производот не постои.';
148 END IF;
149
150 IF v_stock < p_quantity THEN
151 RAISE EXCEPTION 'Нема доволно залиха! Достапни: %, Побарани: %', v_stock, p_quantity;
152 END IF;
153
154 INSERT INTO CinemaOrder (user_id, employee_id, order_date, total_price, status)
155 VALUES (p_user_id, p_employee_id, CURRENT_DATE, v_price * p_quantity, 'PREPARING')
156 RETURNING order_id INTO v_order_id;
157
158 INSERT INTO Order_Product (order_id, product_id, quantity, price_at_order)
159 VALUES (v_order_id, p_product_id, p_quantity, v_price);
160
161 UPDATE Product
162 SET stock_quantity = stock_quantity - p_quantity
163 WHERE product_id = p_product_id;
164
165 RAISE NOTICE 'Нарачката е успешно креирана! Order ID: %', v_order_id;
166END;
167$$;
168
169CALL sp_place_order(1, 1, 5);
170
171CALL sp_place_order(1, 1, 99999);
172
173CALL sp_place_order(1, 99999, 1);
174
175
176
177
178CREATE PROCEDURE pr_cancel_reservation(p_reservation_id INT)
179LANGUAGE plpgsql AS $$
180BEGIN
181
182 DELETE FROM Ticket
183 WHERE reservation_id = p_reservation_id;
184
185 DELETE FROM ReservationPayment
186 WHERE reservation_id = p_reservation_id;
187 UPDATE Reservation
188 SET status = 'CANCELLED'
189 WHERE reservation_id = p_reservation_id;
190 COMMIT;
191END;
192$$;
193SELECT reservation_id, status FROM Reservation WHERE reservation_id = 100;
194CALL pr_cancel_reservation(100);
195SELECT reservation_id, status FROM Reservation WHERE reservation_id = 100;
196SELECT * FROM Ticket WHERE reservation_id = 100;
197
198
199
200
201
202CREATE OR REPLACE PROCEDURE sp_submit_review(
203 p_user_id INT,
204 p_movie_id INT,
205 p_rating INT,
206 p_comment TEXT,
207 p_review_date DATE DEFAULT CURRENT_DATE
208)
209LANGUAGE plpgsql
210AS $$
211BEGIN
212
213 IF NOT EXISTS (
214 SELECT 1
215 FROM Reservation r
216 JOIN Ticket t ON r.reservation_id = t.reservation_id
217 JOIN Showtime s ON t.showtime_id = s.showtime_id
218 WHERE r.user_id = p_user_id
219 AND s.movie_id = p_movie_id
220 AND s.end_time < CURRENT_TIMESTAMP
221 ) THEN
222 RAISE EXCEPTION 'Не можете да оставите рецензија. Или немате купено билет за овој филм, или проекцијата се уште не е завршена.';
223 END IF;
224
225 IF EXISTS (
226 SELECT 1
227 FROM Review
228 WHERE user_id = p_user_id
229 AND movie_id = p_movie_id
230 ) THEN
231 RAISE EXCEPTION 'Веќе оставивте рецензија за овој филм.';
232 END IF;
233
234 IF p_rating < 1 OR p_rating > 10 THEN
235 RAISE EXCEPTION 'Оценката мора да биде помеѓу 1 и 10.';
236 END IF;
237
238 INSERT INTO Review (user_id, movie_id, rating, comment, review_date)
239 VALUES (p_user_id, p_movie_id, p_rating, p_comment, p_review_date);
240
241 RAISE NOTICE 'Рецензијата е успешно зачувана.';
242END;
243$$;
244CALL sp_submit_review(99999, 50, 9, 'Great movie!');
245CALL sp_submit_review(72244, 4781, 8, 'Odlicen film, preporacuvam!');
246CALL sp_submit_review(72244, 4781, 7, 'Vtorata recenzija...');
247CALL sp_submit_review(14667, 4781, 15, 'Super film!');
248
249
250
251
252CREATE OR REPLACE FUNCTION fn_get_dynamic_price(
253 p_showtime_id INT,
254 p_seat_type_id INT,
255 p_user_id INT DEFAULT NULL
256)
257RETURNS INT
258LANGUAGE plpgsql
259AS $$
260DECLARE
261 v_base_price INT;
262 v_total_seats INT;
263 v_sold_seats INT;
264 v_user_tickets INT := 0;
265 v_occupancy NUMERIC;
266 v_price NUMERIC;
267 v_type_mult NUMERIC;
268 v_occ_mult NUMERIC := 1.0;
269 v_bulk_mult NUMERIC := 1.0;
270BEGIN
271 SELECT s.base_price, h.capacity
272 INTO v_base_price, v_total_seats
273 FROM Showtime s
274 JOIN Hall h ON h.hall_id = s.hall_id
275 WHERE s.showtime_id = p_showtime_id;
276
277 IF NOT FOUND THEN
278 RAISE EXCEPTION 'Проекцијата со ID % не постои.', p_showtime_id;
279 END IF;
280
281 SELECT COUNT(*)
282 INTO v_sold_seats
283 FROM Ticket
284 WHERE showtime_id = p_showtime_id;
285
286 v_occupancy := v_sold_seats::NUMERIC / NULLIF(v_total_seats, 0) * 100;
287
288 IF v_occupancy > 60 THEN
289 v_occ_mult := 1.2;
290 END IF;
291
292 v_type_mult := CASE p_seat_type_id
293 WHEN 1 THEN 1.0
294 WHEN 2 THEN 2.0
295 WHEN 3 THEN 1.5
296 WHEN 4 THEN 1.8
297 WHEN 5 THEN 0.7
298 ELSE 1.0
299 END;
300
301 IF p_user_id IS NOT NULL THEN
302 SELECT COUNT(*)
303 INTO v_user_tickets
304 FROM Reservation r
305 JOIN Ticket t ON t.reservation_id = r.reservation_id
306 WHERE r.user_id = p_user_id
307 AND r.showtime_id = p_showtime_id
308 AND r.status != 'CANCELLED';
309 END IF;
310
311 IF (v_user_tickets + 1) > 4 THEN
312 v_bulk_mult := 0.8;
313 END IF;
314
315 v_price := v_base_price * v_type_mult * v_occ_mult * v_bulk_mult;
316
317 RETURN v_price::INT;
318END;
319$$;
320
321SELECT
322 s.showtime_id,
323 m.title,
324 h.name AS sala,
325 h.capacity AS vkupno,
326 COUNT(t.ticket_id) AS prodadeni,
327 ROUND(COUNT(t.ticket_id) * 100.0 / h.capacity, 1) AS procent
328FROM Showtime s
329JOIN Movie m ON m.movie_id = s.movie_id
330JOIN Hall h ON h.hall_id = s.hall_id
331LEFT JOIN Ticket t ON t.showtime_id = s.showtime_id
332WHERE s.start_time > CURRENT_TIMESTAMP
333GROUP BY s.showtime_id, m.title, h.name, h.capacity
334ORDER BY procent DESC
335LIMIT 10;
336
337
338SELECT fn_get_dynamic_price(1, 1) AS cena_standard;
339
340SELECT fn_get_dynamic_price(1, 2) AS cena_vip;
341
342select * from Showtime where showtime_id='1761261';
343SELECT fn_get_dynamic_price(1761261, 1) AS cena_polna_sala;
344
345SELECT fn_get_dynamic_price(1761261, 2) AS cena_vip_polna_sala;
346
347SELECT fn_get_dynamic_price(1761211, 1, 1) AS cena_so_bulk_popust;
348
349SELECT fn_get_dynamic_price(1761261, 2, 1) AS cena_vip_polna_bulk;
350
351SELECT fn_get_dynamic_price(9999999, 1, 1);
352
353
354
355CREATE OR REPLACE PROCEDURE sp_make_reservation(
356 p_user_id INT,
357 p_showtime_id INT,
358 p_seat_id INT,
359 p_employee_id INT DEFAULT 1
360)
361LANGUAGE plpgsql
362AS $$
363DECLARE
364 v_reservation_id INT;
365 v_start_time TIMESTAMP;
366 v_hall_id INT;
367 v_movie_title VARCHAR;
368 v_seat_row INT;
369 v_seat_number INT;
370 v_seat_type VARCHAR;
371 v_base_price INT;
372 v_price INT;
373 v_seat_type_id INT;
374 v_occupancy NUMERIC;
375 v_sold INT;
376 v_capacity INT;
377BEGIN
378 SELECT s.start_time, s.hall_id, s.base_price, m.title
379 INTO v_start_time, v_hall_id, v_base_price, v_movie_title
380 FROM Showtime s
381 JOIN Movie m ON m.movie_id = s.movie_id
382 WHERE s.showtime_id = p_showtime_id;
383
384 IF NOT FOUND THEN
385 RAISE EXCEPTION 'Проекцијата не постои.';
386 END IF;
387
388 IF v_start_time < CURRENT_TIMESTAMP THEN
389 RAISE EXCEPTION 'Не може да се резервира билет за завршена проекција (%).', v_movie_title;
390 END IF;
391
392 IF NOT EXISTS (SELECT 1 FROM CinemaUser WHERE user_id = p_user_id) THEN
393 RAISE EXCEPTION 'Корисникот со ID % не постои.', p_user_id;
394 END IF;
395
396 SELECT se.seat_type_id, se.seat_row, se.seat_number, st.type
397 INTO v_seat_type_id, v_seat_row, v_seat_number, v_seat_type
398 FROM Seat se
399 JOIN Seat_Type st ON st.seat_type_id = se.seat_type_id
400 WHERE se.seat_id = p_seat_id
401 AND se.hall_id = v_hall_id;
402
403 IF NOT FOUND THEN
404 RAISE EXCEPTION 'Седиштето (ID: %) не постои или не припаѓа на салата за оваа проекција.', p_seat_id;
405 END IF;
406
407 IF EXISTS (
408 SELECT 1 FROM Ticket
409 WHERE showtime_id = p_showtime_id
410 AND seat_id = p_seat_id
411 ) THEN
412 RAISE EXCEPTION 'Седиштето Ред % - Број % е веќе зафатено. Изберете друго место.', v_seat_row, v_seat_number;
413 END IF;
414
415 IF EXISTS (
416 SELECT 1
417 FROM Reservation r
418 JOIN Ticket t ON t.reservation_id = r.reservation_id
419 WHERE r.user_id = p_user_id
420 AND r.showtime_id = p_showtime_id
421 AND t.seat_id = p_seat_id
422 AND r.status != 'CANCELLED'
423 ) THEN
424 RAISE EXCEPTION 'Корисникот веќе има резервација за ова седиште (Ред % - Број %).', v_seat_row, v_seat_number;
425 END IF;
426
427 v_price := fn_get_dynamic_price(p_showtime_id, v_seat_type_id, p_user_id);
428
429 SELECT COUNT(*), h.capacity
430 INTO v_sold, v_capacity
431 FROM Ticket t
432 JOIN Showtime s ON s.showtime_id = t.showtime_id
433 JOIN Hall h ON h.hall_id = s.hall_id
434 WHERE t.showtime_id = p_showtime_id
435 GROUP BY h.capacity;
436
437 v_occupancy := COALESCE(v_sold::NUMERIC / NULLIF(v_capacity, 0) * 100, 0);
438
439 INSERT INTO Reservation (user_id, showtime_id, employee_id, reservation_date, status)
440 VALUES (p_user_id, p_showtime_id, p_employee_id, CURRENT_DATE, 'PENDING')
441 RETURNING reservation_id INTO v_reservation_id;
442
443 INSERT INTO Ticket (showtime_id, seat_id, reservation_id, price, purchase_date)
444 VALUES (p_showtime_id, p_seat_id, v_reservation_id, v_price, CURRENT_DATE);
445
446 RAISE NOTICE '=================================';
447 RAISE NOTICE 'Резервацијата е успешно креирана!';
448 RAISE NOTICE 'Филм: %', v_movie_title;
449 RAISE NOTICE 'Седиште: Ред % - Број % (%)', v_seat_row, v_seat_number, v_seat_type;
450 RAISE NOTICE 'Пополнетост: % проценти (% / % места)', ROUND(v_occupancy, 1), COALESCE(v_sold, 0), v_capacity;
451
452 IF v_occupancy > 60 THEN
453 RAISE NOTICE 'Примена динамична цена (+20 проценти поради висока побарувачка)';
454 END IF;
455
456 RAISE NOTICE 'Цена: % ден', v_price;
457 RAISE NOTICE 'Резерв. ID: %', v_reservation_id;
458 RAISE NOTICE 'Статус: PENDING (плаќањето го потврдува)';
459 RAISE NOTICE '=================================';
460
461EXCEPTION
462 WHEN OTHERS THEN
463 RAISE EXCEPTION 'Грешка при резервација: %', SQLERRM;
464END;
465$$;
466
467
468SELECT se.seat_id, se.seat_row, se.seat_number, st.type
469FROM Seat se
470JOIN Seat_Type st ON st.seat_type_id = se.seat_type_id
471WHERE se.hall_id = (SELECT hall_id FROM Showtime WHERE showtime_id = 1761211)
472 AND se.seat_id NOT IN (
473 SELECT seat_id FROM Ticket WHERE showtime_id = 1761211
474 )
475ORDER BY se.seat_row, se.seat_number
476LIMIT 10;
477
478
479CALL sp_make_reservation(1, 1761211, 81, 1);
480CALL sp_make_reservation(1, 1761211, 101, 1);
481CALL sp_make_reservation(1, 1761211, 121, 1);
482CALL sp_make_reservation(1, 1761211, 141, 1);
483CALL sp_make_reservation(1, 1761211, 161, 1);
484
485
486SELECT r.reservation_id, r.status, t.seat_id, t.price
487FROM Reservation r
488JOIN Ticket t ON t.reservation_id = r.reservation_id
489WHERE r.user_id = 1 AND r.showtime_id = 1761211
490ORDER BY r.reservation_id;
491
492
493
494
495CREATE FUNCTION fn_hall_occupancy_report(p_showtime_id INT)
496RETURNS TABLE (
497 red_broj INT,
498 vizuelna_sala TEXT,
499 slobodna_mesta INT,
500 zauzeta_mesta INT,
501 procent_zauzeca NUMERIC
502) AS $$
503BEGIN
504 RETURN QUERY
505 WITH seat_status AS (
506 SELECT
507 s.seat_row,
508 s.seat_number,
509 CASE
510 WHEN EXISTS (
511 SELECT 1 FROM Ticket t
512 WHERE t.seat_id = s.seat_id
513 AND t.showtime_id = p_showtime_id
514 )
515 THEN 'X'
516 ELSE '○'
517 END AS status
518 FROM Seat s
519 JOIN Hall h ON h.hall_id = s.hall_id
520 JOIN Showtime st ON st.hall_id = h.hall_id
521 WHERE st.showtime_id = p_showtime_id
522 ORDER BY s.seat_row, s.seat_number
523 ),
524 row_stats AS (
525 SELECT
526 seat_row,
527 STRING_AGG(status, ' ' ORDER BY seat_number) AS layout,
528 SUM(CASE WHEN status = '○' THEN 1 ELSE 0 END)::INT AS free_count,
529 SUM(CASE WHEN status = 'X' THEN 1 ELSE 0 END)::INT AS occupied_count,
530 COUNT(*)::INT AS total_count
531 FROM seat_status
532 GROUP BY seat_row
533 )
534 SELECT
535 seat_row AS red_broj,
536 layout AS vizuelna_sala,
537 free_count AS slobodna_mesta,
538 occupied_count AS zauzeta_mesta,
539 ROUND((occupied_count::NUMERIC / total_count) * 100, 1) AS procent_zauzeca
540 FROM row_stats
541 ORDER BY seat_row;
542END;
543$$ LANGUAGE plpgsql STABLE;
544
545
546
547
548CREATE OR REPLACE FUNCTION fn_find_best_seat_position(
549 p_showtime_id INT,
550 p_num_seats INT
551)
552RETURNS TABLE (
553 seat_id INT,
554 seat_row INT,
555 seat_number INT,
556 quality VARCHAR,
557 poraka VARCHAR
558)
559LANGUAGE plpgsql
560AS $$
561DECLARE
562 v_hall_id INT;
563 v_total_rows INT;
564 v_max_seats_per_row INT;
565 v_row INT;
566 v_start_seat INT;
567 v_consecutive INT;
568BEGIN
569 IF NOT EXISTS (SELECT 1 FROM Showtime WHERE showtime_id = p_showtime_id) THEN
570 RETURN QUERY SELECT NULL::INT, NULL::INT, NULL::INT, 'NONE'::VARCHAR, 'Прејекција не постои'::VARCHAR;
571 RETURN;
572 END IF;
573
574 SELECT sh.hall_id INTO v_hall_id
575 FROM Showtime sh
576 WHERE sh.showtime_id = p_showtime_id;
577
578 SELECT COALESCE(MAX(s.seat_row), 0), COALESCE(MAX(s.seat_number), 0)
579 INTO v_total_rows, v_max_seats_per_row
580 FROM Seat s
581 WHERE s.hall_id = v_hall_id;
582
583 FOR v_row IN 1..v_total_rows LOOP
584 FOR v_start_seat IN 1..(v_max_seats_per_row - p_num_seats + 1) LOOP
585
586 SELECT COUNT(*) INTO v_consecutive
587 FROM Seat s
588 WHERE s.hall_id = v_hall_id
589 AND s.seat_row = v_row
590 AND s.seat_number >= v_start_seat
591 AND s.seat_number < v_start_seat + p_num_seats
592 AND NOT EXISTS (
593 SELECT 1 FROM Ticket t
594 WHERE t.seat_id = s.seat_id
595 AND t.showtime_id = p_showtime_id
596 );
597
598 IF v_consecutive = p_num_seats THEN
599 RETURN QUERY
600 SELECT
601 s.seat_id,
602 s.seat_row,
603 s.seat_number,
604 'AVAILABLE'::VARCHAR,
605 '✓ Место достапно'::VARCHAR
606 FROM Seat s
607 WHERE s.hall_id = v_hall_id
608 AND s.seat_row = v_row
609 AND s.seat_number >= v_start_seat
610 AND s.seat_number < v_start_seat + p_num_seats
611 AND NOT EXISTS (
612 SELECT 1 FROM Ticket t
613 WHERE t.seat_id = s.seat_id
614 AND t.showtime_id = p_showtime_id
615 )
616 ORDER BY s.seat_number;
617 RETURN;
618 END IF;
619
620 END LOOP;
621 END LOOP;
622
623 RETURN QUERY SELECT NULL::INT, NULL::INT, NULL::INT, 'NONE'::VARCHAR,
624 ('Нема ' || p_num_seats || ' последователни седишта во ист ред. Проверете во друг showtime или помал број на седишта.')::VARCHAR;
625END;
626$$;
627
628
629SELECT * FROM fn_find_best_seat_position(1761211, 3);
630SELECT * FROM fn_find_best_seat_position(1761211, 5);
631SELECT * FROM fn_find_best_seat_position(1761211, 1);
632SELECT * FROM fn_find_best_seat_position(9999999, 2);
633
634
635SELECT * FROM fn_hall_occupancy_report(1761247);
636
637
638SELECT * FROM fn_find_best_seat_position(1761247, 3);
639
640
641SELECT * FROM fn_find_best_seat_position(1761247, 5);
642
643
644SELECT * FROM fn_find_best_seat_position(1761247, 8);
645
646
647
648
649CREATE OR REPLACE FUNCTION prevent_seat_double_booking()
650RETURNS TRIGGER AS $$
651BEGIN
652 IF EXISTS (
653 SELECT 1 FROM Ticket
654 WHERE showtime_id = NEW.showtime_id
655 AND seat_id = NEW.seat_id
656 ) THEN
657 RAISE EXCEPTION 'ERROR: Seat % is already booked for showtime %',
658 NEW.seat_id, NEW.showtime_id;
659 END IF;
660
661 IF NOT EXISTS (
662 SELECT 1 FROM Seat s
663 JOIN Hall h ON h.hall_id = s.hall_id
664 JOIN Showtime st ON st.hall_id = h.hall_id
665 WHERE s.seat_id = NEW.seat_id
666 AND st.showtime_id = NEW.showtime_id
667 ) THEN
668 RAISE EXCEPTION 'ERROR: Seat % does not belong to the hall of showtime %',
669 NEW.seat_id, NEW.showtime_id;
670 END IF;
671
672 RETURN NEW;
673END;
674$$ LANGUAGE plpgsql;
675
676
677CREATE TRIGGER trg_prevent_seat_double_booking
678BEFORE INSERT ON Ticket
679FOR EACH ROW
680EXECUTE FUNCTION prevent_seat_double_booking();
681
682INSERT INTO Ticket (showtime_id, seat_id, reservation_id, price, purchase_date)
683VALUES (2, 222, 6, 300, '2025-01-01');
684
685INSERT INTO Ticket (showtime_id, seat_id, reservation_id, price, purchase_date)
686VALUES (2, 222, 6, 300, '2025-01-02');
687
688
689SELECT *
690FROM Ticket
691WHERE showtime_id = 2
692 AND seat_id = 222;
693
694
695
696
697CREATE OR REPLACE FUNCTION prevent_showtime_overlap()
698RETURNS TRIGGER AS $$
699DECLARE
700 v_overlapping_count INT;
701BEGIN
702
703
704 SELECT COUNT(*)
705 INTO v_overlapping_count
706 FROM Showtime
707 WHERE hall_id = NEW.hall_id
708 AND showtime_id != COALESCE(NEW.showtime_id, -1)
709
710 AND NOT (NEW.end_time <= start_time OR NEW.start_time >= end_time);
711
712 IF v_overlapping_count > 0 THEN
713 RAISE EXCEPTION 'ERROR: Showtime overlaps with % existing showtime(s) in hall %',
714 v_overlapping_count, NEW.hall_id;
715 END IF;
716
717 IF NEW.start_time >= NEW.end_time THEN
718 RAISE EXCEPTION 'ERROR: Start time must be before end time';
719 END IF;
720
721 RETURN NEW;
722END;
723$$ LANGUAGE plpgsql;
724
725
726DROP TRIGGER IF EXISTS trg_prevent_showtime_overlap_insert ON Showtime;
727DROP TRIGGER IF EXISTS trg_prevent_showtime_overlap_update ON Showtime;
728
729CREATE TRIGGER trg_prevent_showtime_overlap_insert
730BEFORE INSERT ON Showtime
731FOR EACH ROW
732EXECUTE FUNCTION prevent_showtime_overlap();
733
734CREATE TRIGGER trg_prevent_showtime_overlap_update
735BEFORE UPDATE ON Showtime
736FOR EACH ROW
737EXECUTE FUNCTION prevent_showtime_overlap();
738
739
740INSERT INTO Showtime (movie_id, hall_id, start_time, end_time, base_price)
741VALUES (1, 1, '2026-06-01 10:00:00', '2026-06-01 12:00:00', 400);
742
743INSERT INTO Showtime (movie_id, hall_id, start_time, end_time, base_price)
744VALUES (2, 1, '2026-06-01 11:30:00', '2026-06-01 13:30:00', 450);
745
746
747UPDATE Showtime SET start_time = '2026-06-01 11:30:00'
748WHERE showtime_id = (SELECT MAX(showtime_id) FROM Showtime);
749
750
751SELECT showtime_id, hall_id, start_time, end_time
752FROM Showtime
753WHERE hall_id = 1
754 AND start_time::DATE = '2026-06-01'
755ORDER BY start_time;
756
757
758
759CREATE OR REPLACE FUNCTION check_reservation_before_showtime()
760RETURNS TRIGGER AS $$
761DECLARE
762 v_start_time TIMESTAMP;
763BEGIN
764 SELECT s.start_time INTO v_start_time
765 FROM Showtime s
766 WHERE s.showtime_id = NEW.showtime_id;
767
768 IF v_start_time IS NULL THEN
769 RAISE EXCEPTION 'Прејекцијата не постои.';
770 END IF;
771
772 IF NEW.reservation_date >= v_start_time::DATE THEN
773 RAISE EXCEPTION 'Резервацијата (%) мора да биде пред почетокот на прејекцијата (%)!',
774 NEW.reservation_date, v_start_time;
775 END IF;
776
777 RETURN NEW;
778END;
779$$ LANGUAGE plpgsql;
780
781DROP TRIGGER IF EXISTS trg_check_reservation_before_showtime ON Reservation;
782CREATE TRIGGER trg_check_reservation_before_showtime
783BEFORE INSERT OR UPDATE ON Reservation
784FOR EACH ROW
785EXECUTE FUNCTION check_reservation_before_showtime();
786
787
788CREATE OR REPLACE FUNCTION check_payment_after_reservation()
789RETURNS TRIGGER AS $$
790DECLARE
791 v_reservation_date DATE;
792BEGIN
793 SELECT r.reservation_date INTO v_reservation_date
794 FROM Reservation r
795 WHERE r.reservation_id = NEW.reservation_id;
796
797 IF v_reservation_date IS NULL THEN
798 RAISE EXCEPTION 'Резервацијата не постои.';
799 END IF;
800
801 IF NEW.payment_date < v_reservation_date THEN
802 RAISE EXCEPTION 'Плаќањето (%) не може да биде пред резервацијата (%)!',
803 NEW.payment_date, v_reservation_date;
804 END IF;
805
806 RETURN NEW;
807END;
808$$ LANGUAGE plpgsql;
809
810DROP TRIGGER IF EXISTS trg_check_payment_after_reservation ON ReservationPayment;
811CREATE TRIGGER trg_check_payment_after_reservation
812BEFORE INSERT OR UPDATE ON ReservationPayment
813FOR EACH ROW
814EXECUTE FUNCTION check_payment_after_reservation();
815
816CALL sp_buy_ticket(1, 1761265, 1199, 'Cash');
817
818
819INSERT INTO Reservation (user_id, showtime_id, employee_id, reservation_date, status)
820VALUES (1, 1, 1, '2000-01-01', 'PENDING');
821
822
823INSERT INTO ReservationPayment (user_id, reservation_id, employee_id, amount, payment_date, payment_method)
824VALUES (1, 10000001, 1, 500, '2000-01-01', 'Credit Card');
825
826
827INSERT INTO ReservationPayment (user_id, reservation_id, employee_id, amount, payment_date, payment_method)
828VALUES (1, 10000001, 1, 500, CURRENT_DATE + 3, 'Online');