DatabaseProgramming: funkcii,proceduri,trigeri.sql

File funkcii,proceduri,trigeri.sql, 20.1 KB (added by 231001, 3 days ago)
Line 
1----------------------------------------------------------------------------------------funkcii--------------------------------------------------------------------
2
3-- 1. funkcija za vkupna cena za rezervacija, i plus i so equipment ako rentat, ako sakat da koristit dopolnitelen service, plus i za activities ako ima rezervacija
4CREATE OR REPLACE FUNCTION calculate_full_reservation_price(
5 p_reservation_id INT
6)
7RETURNS TABLE (
8 base_price NUMERIC,
9 activity_price NUMERIC,
10 equipment_price NUMERIC,
11 service_price NUMERIC,
12 discount_amount NUMERIC,
13 discount_percent INT,
14 final_price NUMERIC
15) AS $$
16DECLARE
17 v_base_price NUMERIC := 0;
18 v_activity_price NUMERIC := 0;
19 v_equipment_price NUMERIC := 0;
20 v_service_price NUMERIC := 0;
21 v_discount_percent INT := 0;
22 v_discount_amount NUMERIC := 0;
23 v_final_price NUMERIC := 0;
24 v_campLocationId INTEGER;
25BEGIN
26
27 SELECT total_price, CampLocationcamplocationId
28 INTO v_base_price, v_campLocationId
29 FROM Reservation
30 WHERE reservationId = p_reservation_id;
31
32 IF NOT FOUND THEN
33 RAISE EXCEPTION 'Reservation with ID % does not exist!', p_reservation_id;
34 END IF;
35
36 SELECT COALESCE(SUM(total_price_for_activity), 0)
37 INTO v_activity_price
38 FROM ReservationActivity
39 WHERE ReservationreservationId = p_reservation_id
40 AND status <> 'cancelled';
41
42 SELECT COALESCE(SUM(total_price), 0)
43 INTO v_equipment_price
44 FROM EquipmentRental
45 WHERE ReservationreservationId = p_reservation_id
46 AND status <> 'cancelled';
47
48 SELECT COALESCE(SUM(s.additional_fee), 0)
49 INTO v_service_price
50 FROM camp_location_service cls
51 JOIN Service s ON cls.ServiceserviceId = s.serviceId
52 WHERE cls.CampLocationcampLocationId = v_campLocationId;
53
54 SELECT
55 COALESCE(p.discount_percent, 0),
56 COALESCE(p.discount_amount, 0)
57 INTO
58 v_discount_percent,
59 v_discount_amount
60 FROM Reservation r
61 LEFT JOIN Promotion p ON r.PromotionpromotionId = p.promotionId
62 WHERE r.reservationId = p_reservation_id;
63
64 v_final_price :=
65 v_base_price +
66 v_activity_price +
67 v_equipment_price +
68 v_service_price;
69
70 IF v_discount_percent > 0 THEN
71 v_final_price := v_final_price - (v_final_price * v_discount_percent / 100.0);
72 END IF;
73
74 IF v_discount_amount > 0 THEN
75 v_final_price := v_final_price - v_discount_amount;
76 END IF;
77
78 IF v_final_price < 0 THEN
79 v_final_price := 0;
80 END IF;
81
82 base_price := ROUND(v_base_price, 2);
83 activity_price := ROUND(v_activity_price, 2);
84 equipment_price := ROUND(v_equipment_price, 2);
85 service_price := ROUND(v_service_price, 2);
86 discount_amount := ROUND(v_discount_amount, 2);
87 discount_percent := v_discount_percent;
88 final_price := ROUND(v_final_price, 2);
89
90 RETURN NEXT;
91
92END;
93$$ LANGUAGE plpgsql;
94
95--test
96SELECT * FROM calculate_full_reservation_price(2);
97
98
99-- 2. funkcija za dostapnost na camplocation za odreden period
100CREATE OR REPLACE FUNCTION is_camp_available(
101 p_campLocationId INTEGER,
102 p_check_in DATE,
103 p_check_out DATE
104)
105RETURNS BOOLEAN AS $$
106DECLARE
107 v_conflict INTEGER;
108 v_status VARCHAR(20);
109BEGIN
110
111 IF p_check_out <= p_check_in THEN
112 RAISE EXCEPTION
113 'Check-out date must be after check-in date!';
114 END IF;
115
116 SELECT status
117 INTO v_status
118 FROM CampLocation
119 WHERE campLocationId = p_campLocationId;
120
121 IF NOT FOUND THEN
122 RAISE EXCEPTION
123 'Camp location with ID % does not exist!',
124 p_campLocationId;
125 END IF;
126
127 IF v_status <> 'active' THEN
128 RETURN FALSE;
129 END IF;
130
131 SELECT COUNT(*)
132 INTO v_conflict
133 FROM Reservation
134 WHERE CampLocationcamplocationId = p_campLocationId
135 AND reservation_status IN ('pending', 'confirmed')
136 AND check_in_date < p_check_out
137 AND check_out_date > p_check_in;
138
139 IF v_conflict > 0 THEN
140 RETURN FALSE;
141 END IF;
142
143 RETURN TRUE;
144
145END;
146$$ LANGUAGE plpgsql;
147
148--test
149SELECT is_camp_available(11, '2026-08-18', '2026-08-21');
150SELECT camplocationcamplocationid, check_in_date, check_out_date, reservation_status
151FROM Reservation
152WHERE reservation_status IN ('pending', 'confirmed')
153LIMIT 10;
154
155
156
157
158------------------------------------------------------------------------------------proceduri----------------------------------------------------------------------------------------------------------
159
160-- 1. procedura za kreiranje rezervacija
161CREATE OR REPLACE PROCEDURE make_reservation(
162 p_guestUserId INTEGER,
163 p_campLocationId INTEGER,
164 p_check_in DATE,
165 p_check_out DATE,
166 p_num_guests INTEGER,
167 p_promo_code VARCHAR(50) DEFAULT NULL
168)
169LANGUAGE plpgsql AS $$
170DECLARE
171 v_min_nights INTEGER;
172 v_max_guests INTEGER;
173 v_nights INTEGER;
174 v_price_per_night NUMERIC(12, 2);
175 v_total_price NUMERIC(12, 2);
176 v_promotionId INTEGER := NULL;
177 v_discount NUMERIC(10, 2) := 0;
178 v_new_res_id INTEGER;
179BEGIN
180
181 IF NOT is_camp_available(p_campLocationId, p_check_in, p_check_out) THEN
182 RAISE EXCEPTION 'Camp location not available for the chosen period!';
183 END IF;
184
185 SELECT min_nights_stay, max_guests
186 INTO v_min_nights, v_max_guests
187 FROM CampLocation
188 WHERE campLocationId = p_campLocationId;
189
190 IF p_num_guests > v_max_guests THEN
191 RAISE EXCEPTION 'Maximum number of guests is %!', v_max_guests;
192 END IF;
193
194 SELECT ph.price_per_night
195 INTO v_price_per_night
196 FROM PriceHistory ph
197 WHERE ph.CampLocationcampLocationId = p_campLocationId
198 AND p_check_in BETWEEN ph.date_from AND ph.date_to
199 LIMIT 1;
200
201 IF v_price_per_night IS NULL THEN
202 RAISE EXCEPTION 'There is no active price for the chosen camp location!';
203 END IF;
204
205 v_nights := p_check_out - p_check_in;
206 v_total_price := v_nights * v_price_per_night;
207
208 IF p_promo_code IS NOT NULL THEN
209 SELECT p.promotionId,
210 COALESCE(p.discount_amount, 0) +
211 COALESCE(v_total_price * p.discount_percent / 100.0, 0)
212 INTO v_promotionId, v_discount
213 FROM Promotion p
214 WHERE p.promo_code = p_promo_code
215 AND p.valid_from <= CURRENT_DATE
216 AND p.valid_to >= CURRENT_DATE
217 LIMIT 1;
218
219 IF NOT FOUND THEN
220 RAISE EXCEPTION 'The promo code "%" is not valid or is expired!', p_promo_code;
221 END IF;
222 END IF;
223
224 v_total_price := GREATEST(v_total_price - v_discount, 0);
225
226 INSERT INTO Reservation (
227 GuestUseruserId,
228 CampLocationcamplocationId,
229 PromotionpromotionId,
230 number_of_guests,
231 reservation_status,
232 check_in_date,
233 check_out_date,
234 created_at,
235 total_price
236 )
237 VALUES (
238 p_guestUserId,
239 p_campLocationId,
240 v_promotionId,
241 p_num_guests,
242 'pending',
243 p_check_in,
244 p_check_out,
245 LEAST(CURRENT_DATE, p_check_in - 1),
246 v_total_price
247 )
248 RETURNING reservationId INTO v_new_res_id;
249
250 RAISE NOTICE 'Reservation is successfully created! ID: %, Total price: % EUR',
251 v_new_res_id, v_total_price;
252
253 COMMIT;
254END;
255$$;
256
257--test
258--guest
259SELECT user_id FROM "User" WHERE type = 'guest' LIMIT 5;
260--camplocation
261SELECT campLocationId, min_nights_stay, max_guests FROM CampLocation LIMIT 5;
262--za price vo toj period
263SELECT * FROM PriceHistory WHERE CampLocationcampLocationId = 1 LIMIT 5;
264
265CALL make_reservation(
266 423,
267 1,
268 '2023-04-11',
269 '2023-05-01',
270 2,
271 NULL
272);
273
274
275-- 2. procedura za dodavanje promotion_code
276CREATE OR REPLACE PROCEDURE add_promotion(
277 p_hostUserId INTEGER,
278 p_promo_code VARCHAR(50),
279 p_valid_from DATE,
280 p_valid_to DATE,
281 p_discount_amount NUMERIC(10,2) DEFAULT 0,
282 p_discount_percent NUMERIC(5,2) DEFAULT 0
283)
284LANGUAGE plpgsql AS $$
285BEGIN
286
287 IF NOT EXISTS (SELECT 1 FROM Host WHERE UseruserId = p_hostUserId) THEN
288 RAISE EXCEPTION 'Host with ID % does not exist!', p_hostUserId;
289 END IF;
290
291 IF EXISTS (SELECT 1 FROM Promotion WHERE promo_code = p_promo_code) THEN
292 RAISE EXCEPTION 'Promo code "%" already exists!', p_promo_code;
293 END IF;
294
295 IF p_valid_from > p_valid_to THEN
296 RAISE EXCEPTION 'Invalid date range: valid_from > valid_to!';
297 END IF;
298
299 IF p_discount_percent = 0 AND p_discount_amount = 0 THEN
300 RAISE EXCEPTION 'At least one discount must be provided!';
301 END IF;
302
303 IF p_discount_percent < 0 OR p_discount_percent > 100 THEN
304 RAISE EXCEPTION 'Discount percent must be between 0 and 100!';
305 END IF;
306
307 IF p_discount_amount < 0 THEN
308 RAISE EXCEPTION 'Discount amount cannot be negative!';
309 END IF;
310
311 INSERT INTO Promotion (
312 HostUserUserId,
313 promo_code,
314 discount_amount,
315 discount_percent,
316 valid_from,
317 valid_to
318 )
319 VALUES (
320 p_hostUserId,
321 p_promo_code,
322 p_discount_amount,
323 p_discount_percent,
324 p_valid_from,
325 p_valid_to
326 );
327
328 RAISE NOTICE 'Promotion "%" successfully created!', p_promo_code;
329
330 COMMIT;
331END;
332$$;
333
334--test
335SELECT UseruserId FROM Host LIMIT 5;
336CALL add_promotion(
337 634,
338 'SUMMER2026',
339 '2026-01-01',
340 '2026-12-31',
341 0,
342 10
343);
344CALL add_promotion(
345 634,
346 'SUMMER2026v2',
347 '2026-01-01',
348 '2026-12-31',
349 30,
350 0
351);
352--ist promo code
353CALL add_promotion(
354 634,
355 'SUMMER2026',
356 '2026-01-01',
357 '2026-12-31',
358 0,
359 100
360);
361--bez discount_amount i discount_percent
362CALL add_promotion(
363 634,
364 'SUMMER2026_promotion',
365 '2026-01-01',
366 '2026-12-31',
367 0,
368 0
369);
370
371
372-- 3. procedura za iznajmuvanje equipment za postoecka rezervacija
373CREATE OR REPLACE PROCEDURE rent_equipment(
374 p_reservationId INTEGER,
375 p_equipmentId INTEGER,
376 p_start_date DATE,
377 p_end_date DATE,
378 p_quantity INTEGER
379)
380LANGUAGE plpgsql AS $$
381DECLARE
382 v_price_per_day NUMERIC(10,2);
383 v_deposit NUMERIC(10,2);
384 v_available_qty INTEGER;
385 v_days INTEGER;
386 v_total_price NUMERIC(10,2);
387 v_res_status VARCHAR(20);
388BEGIN
389
390 SELECT reservation_status
391 INTO v_res_status
392 FROM Reservation
393 WHERE reservationId = p_reservationId;
394
395 IF NOT FOUND THEN
396 RAISE EXCEPTION 'Reservation with ID % does not exist!', p_reservationId;
397 END IF;
398
399 IF v_res_status NOT IN ('pending', 'confirmed') THEN
400 RAISE EXCEPTION 'Cannot rent equipment for reservation with status: %', v_res_status;
401 END IF;
402
403 SELECT rental_price_per_day, deposit_amount, available_quantity
404 INTO v_price_per_day, v_deposit, v_available_qty
405 FROM Equipment
406 WHERE equipmentId = p_equipmentId;
407
408 IF NOT FOUND THEN
409 RAISE EXCEPTION 'Equipment with ID % does not exist!', p_equipmentId;
410 END IF;
411
412 IF p_quantity > v_available_qty THEN
413 RAISE EXCEPTION 'Not enough equipment available! Requested: %, Available: %',
414 p_quantity, v_available_qty;
415 END IF;
416
417 IF p_start_date >= p_end_date THEN
418 RAISE EXCEPTION 'Invalid rental period: end date must be after start date!';
419 END IF;
420
421 v_days := p_end_date - p_start_date;
422 v_total_price := v_days * v_price_per_day * p_quantity;
423
424 INSERT INTO EquipmentRental (
425 EquipmentequipmentId,
426 ReservationreservationId,
427 quantity,
428 start_date,
429 end_date,
430 total_price,
431 deposit_paid,
432 status
433 )
434 VALUES (
435 p_equipmentId,
436 p_reservationId,
437 p_quantity,
438 p_start_date,
439 p_end_date,
440 v_total_price,
441 v_deposit * p_quantity,
442 'active'
443 );
444
445 UPDATE Equipment
446 SET available_quantity = available_quantity - p_quantity
447 WHERE equipmentId = p_equipmentId;
448
449 RAISE NOTICE 'Equipment rented successfully. Total: % EUR', v_total_price;
450
451 COMMIT;
452END;
453$$;
454
455--test
456SELECT equipmentId, available_quantity, rental_price_per_day FROM Equipment
457WHERE available_quantity > 0 LIMIT 5;
458
459SELECT reservationId FROM Reservation
460WHERE reservation_status IN ('pending', 'confirmed')
461LIMIT 5;
462
463CALL rent_equipment(
464 2,
465 2,
466 '2026-06-01',
467 '2026-06-05',
468 2
469);
470
471SELECT equipmentId, available_quantity
472FROM Equipment
473WHERE equipmentId = 2;
474
475--4. procedura za koga kje se vrati equipmentrental, da se promeni statusot 'rented' -> 'returned' i da se zgolemi available_quantity na equipment
476CREATE OR REPLACE PROCEDURE return_equipment(
477 p_equipmentRentalId INTEGER
478)
479LANGUAGE plpgsql AS $$
480DECLARE
481 v_equipmentId INTEGER;
482 v_quantity INTEGER;
483 v_status VARCHAR(20);
484BEGIN
485 SELECT EquipmentequipmentId, quantity, status
486 INTO v_equipmentId, v_quantity, v_status
487 FROM EquipmentRental
488 WHERE equipmentrentalid = p_equipmentRentalId;
489
490 IF NOT FOUND THEN
491 RAISE EXCEPTION 'Equipment rental with ID % does not exist!', p_equipmentRentalId;
492 END IF;
493
494 IF v_status = 'returned' THEN
495 RAISE EXCEPTION 'Equipment is already returned!';
496 END IF;
497
498 UPDATE EquipmentRental
499 SET status = 'returned'
500 WHERE equipmentrentalid = p_equipmentRentalId;
501
502 UPDATE Equipment
503 SET available_quantity = LEAST(available_quantity + v_quantity, total_quantity)
504 WHERE equipmentId = v_equipmentId;
505
506 RAISE NOTICE 'Equipment ID % returned successfully. Quantity restored: %.',
507 v_equipmentId, v_quantity;
508
509 COMMIT;
510END;
511$$;
512
513--test
514SELECT er.equipmentrentalid, er.EquipmentequipmentId, er.quantity, e.available_quantity, e.total_quantity
515FROM EquipmentRental er
516JOIN Equipment e ON e.equipmentId = er.EquipmentequipmentId
517WHERE er.status = 'active'
518AND e.available_quantity < e.total_quantity
519LIMIT 5;
520--equipmentrentalId=35,equipmentId=36, quantity=2
521--quantity pred e 0
522SELECT equipmentId, available_quantity
523FROM Equipment
524WHERE equipmentId = 36;
525---
526CALL return_equipment(35);
527--proverk posle, quantity se zgolemi za 2
528SELECT equipmentId, available_quantity, total_quantity
529FROM Equipment
530WHERE equipmentId = 36;
531--proverka na status
532SELECT equipmentrentalid, status
533FROM EquipmentRental
534WHERE equipmentrentalid = 35;
535
536
537--5. procedura za da se zgolemi num_reservations koga kje se kreira nova rezervacija kaj Guest
538CREATE OR REPLACE PROCEDURE add_reservation_count(
539 p_guestUserId INTEGER
540)
541LANGUAGE plpgsql AS $$
542DECLARE
543 v_current_count INTEGER;
544BEGIN
545 IF NOT EXISTS (SELECT 1 FROM Guest WHERE UseruserId = p_guestUserId) THEN
546 RAISE EXCEPTION 'Guest with ID % does not exist!', p_guestUserId;
547 END IF;
548
549 SELECT num_reservations
550 INTO v_current_count
551 FROM Guest
552 WHERE UseruserId = p_guestUserId;
553
554 UPDATE Guest
555 SET num_reservations = num_reservations + 1
556 WHERE UseruserId = p_guestUserId;
557
558 RAISE NOTICE 'Guest % reservation count updated from % to %.',
559 p_guestUserId, v_current_count, v_current_count + 1;
560
561 COMMIT;
562END;
563$$;
564
565--test
566SELECT UseruserId, num_reservations
567FROM Guest
568WHERE UseruserId = 1663;
569--num_reservations=22
570
571CALL add_reservation_count(1663);
572
573SELECT UseruserId, num_reservations
574FROM Guest
575WHERE UseruserId = 1663;
576--num_reservations=23
577
578
579
580--6.
581CREATE OR REPLACE PROCEDURE refund_payment(
582 p_paymentId INTEGER,
583 p_refund_amount NUMERIC(12,2) DEFAULT NULL
584)
585LANGUAGE plpgsql AS $$
586DECLARE
587 v_payment_status VARCHAR(30);
588 v_amount NUMERIC(12,2);
589 v_reservationId INTEGER;
590 v_reservation_status VARCHAR(20);
591 v_refund_amount NUMERIC(12,2);
592BEGIN
593 SELECT payment_status, amount, ReservationreservationId
594 INTO v_payment_status, v_amount, v_reservationId
595 FROM Payment
596 WHERE paymentId = p_paymentId;
597
598 IF NOT FOUND THEN
599 RAISE EXCEPTION 'Payment with ID % does not exist!', p_paymentId;
600 END IF;
601
602 IF v_payment_status NOT IN ('completed', 'partially_refunded') THEN
603 RAISE EXCEPTION 'Payment with status "%" cannot be refunded!', v_payment_status;
604 END IF;
605
606 SELECT reservation_status
607 INTO v_reservation_status
608 FROM Reservation
609 WHERE reservationId = v_reservationId;
610
611 IF v_reservation_status != 'cancelled' THEN
612 RAISE EXCEPTION 'Refund is only allowed for cancelled reservations!';
613 END IF;
614
615 IF p_refund_amount IS NULL THEN
616 v_refund_amount := v_amount;
617 ELSE
618 v_refund_amount := p_refund_amount;
619 END IF;
620
621 IF v_refund_amount > v_amount THEN
622 RAISE EXCEPTION 'Refund amount (%) cannot exceed payment amount (%)!',
623 v_refund_amount, v_amount;
624 END IF;
625
626 IF v_refund_amount <= 0 THEN
627 RAISE EXCEPTION 'Refund amount must be greater than 0!';
628 END IF;
629
630 UPDATE Payment
631 SET payment_status = CASE
632 WHEN v_refund_amount = v_amount THEN 'refunded'
633 ELSE 'partially_refunded'
634 END
635 WHERE paymentId = p_paymentId;
636
637 UPDATE ReservationStatus
638 SET refund_amount = v_refund_amount,
639 cancellation_date = CURRENT_DATE
640 WHERE ReservationreservationId = v_reservationId;
641
642 RAISE NOTICE 'Payment % refunded successfully. Refund amount: % EUR.',
643 p_paymentId, v_refund_amount;
644
645 COMMIT;
646END;
647$$;
648
649--test
650SELECT p.paymentId, p.amount, p.payment_status, r.reservation_status
651FROM Payment p
652JOIN Reservation r ON r.reservationId = p.ReservationreservationId
653WHERE r.reservation_status = 'cancelled'
654AND p.payment_status = 'completed'
655LIMIT 5;
656--paymentId=156, amount 317
657CALL refund_payment(156);
658--proverka na status
659SELECT paymentId, amount, payment_status
660FROM Payment
661WHERE paymentId = 156;
662-- proveri refund_amount vo ReservationStatus
663SELECT rs.ReservationreservationId, rs.refund_amount, rs.cancellation_date
664FROM ReservationStatus rs
665JOIN Payment p ON p.ReservationreservationId = rs.ReservationreservationId
666WHERE p.paymentId = 156;
667
668-------------------------------------------------------------------------------------trigeri-----------------------------------------------------------------------------------------------------------
669-- 1. triger za avtomatsko azuriranje na average rating na Host, koga kje se vnese review se presmetuva nov average rating za Host
670
671CREATE OR REPLACE FUNCTION update_host_rating()
672RETURNS TRIGGER AS $$
673DECLARE
674 v_host_id INTEGER;
675 v_avg NUMERIC(3,2);
676BEGIN
677 SELECT h.HostUseruserid
678 INTO v_host_id
679 FROM Host_Host h
680 WHERE h.CampLocationcampLocationId = NEW.CampLocationcampLocationId
681 LIMIT 1;
682
683 SELECT ROUND(AVG(r.rating), 2)
684 INTO v_avg
685 FROM Review r
686 WHERE r.CampLocationcampLocationId = NEW.CampLocationcampLocationId;
687
688 UPDATE Host
689 SET average_score = v_avg
690 WHERE UseruserId = v_host_id;
691
692 REFRESH MATERIALIZED VIEW view_camp_avg_rating;
693
694 RAISE NOTICE 'Host % average score updated to %. Materialized view refreshed.',
695 v_host_id, v_avg;
696
697 RETURN NEW;
698END;
699$$ LANGUAGE plpgsql;
700
701
702CREATE TRIGGER trg_update_host_rating
703AFTER INSERT ON Review
704FOR EACH ROW
705EXECUTE FUNCTION update_host_rating();
706
707--test
708--barame camp sho imat reviews
709SELECT CampLocationcampLocationId, COUNT(*), AVG(rating)
710FROM Review
711GROUP BY CampLocationcampLocationId
712LIMIT 5;
713--barame current host rating
714SELECT UseruserId, average_score
715FROM Host
716WHERE UseruserId = (
717 SELECT HostUseruserid
718 FROM Host_Host
719 WHERE CampLocationcampLocationId = 39224
720 limit 1
721);
722--avg_score e 4
723INSERT INTO Review (
724 guestuseruserid,
725 CampLocationcampLocationId,
726 review_date,
727 comment,
728 rating
729)
730VALUES (
731 1663,
732 39224,
733 CURRENT_DATE,
734 'Great experience!',
735 5
736);
737--sega avg_score e 3
738SELECT UseruserId, average_score
739FROM Host
740WHERE UseruserId = (
741 SELECT HostUseruserid
742 FROM Host_Host
743 WHERE CampLocationcampLocationId = 39224
744 limit 1
745);