Changes between Version 5 and Version 6 of DatabaseProgramming
- Timestamp:
- 05/27/26 00:29:05 (14 hours ago)
Legend:
- Unmodified
- Added
- Removed
- Modified
-
DatabaseProgramming
v5 v6 5 5 6 6 7 = Функции, Процедури и Тригери = 7 8 Во оваа фаза беа имплементирани функции, процедури и тригери кои ја поддржуваат основната бизнис логика на системот за booking на артисти и бендови. Секоја компонента е одговорна за одредена операција — од пресметување статистики, преку управување со bookings, до автоматска заштита од грешки. 8 9 … … 78 79 Функцијата {{{fn_check_artist_availability}}} проверува дали артист или бенд има слободен термин на одреден датум и враќа {{{TRUE}}} или {{{FALSE}}}. 79 80 80 Наместо {{{COUNT(*)}}} кој ги брои сите редици, функцијата користи {{{EXISTS}}} кој застанува веднаш при првиот пронајден слободен слот — со што е значително поефикасна.81 Пребарува во табелата {{{AvailabilitySlot}}} дали постои слот со статус {{{AVAILABLE}}} за дадениот артист на бараниот датум. Резултатот од {{{COUNT(*)}}} се споредува со {{{0}}} и се враќа соодветна булова вредност. 81 82 {{{ 82 83 CREATE OR REPLACE FUNCTION fn_check_artist_availability( 83 84 p_bookable_id INT, 84 p_date DATE85 p_date DATE 85 86 ) 86 87 RETURNS BOOLEAN … … 88 89 $$ 89 90 DECLARE 90 v_exists BOOLEAN; 91 BEGIN 92 93 SELECT EXISTS( 94 SELECT 1 95 FROM AvailabilitySlot 96 WHERE bookable_id = p_bookable_id 97 AND DATE(start_datetime) = p_date 98 AND status = 'AVAILABLE' 99 ) 100 INTO v_exists; 101 102 RETURN v_exists; 91 v_exists INT; 92 BEGIN 93 94 SELECT COUNT(*) 95 INTO v_exists 96 FROM AvailabilitySlot 97 WHERE bookable_id = p_bookable_id 98 AND DATE(start_datetime) = p_date 99 AND status = 'AVAILABLE'; 100 101 RETURN v_exists > 0; 103 102 104 103 END; … … 110 109 Функцијата {{{fn_total_client_bookings}}} го враќа вкупниот број на bookings направени од одреден клиент. 111 110 112 Ги поврзува табелите {{{BookingRequest}}}, {{{Offer}}} и {{{Booking}}} за да го пресмета бројот на сите bookings поврзани со клиентот, без разлика на нивниотстатус.111 Ги поврзува табелите {{{BookingRequest}}}, {{{Offer}}} и {{{Booking}}} и го пресметува бројот на сите bookings поврзани со клиентот, без разлика на нивниот тековен статус. 113 112 {{{ 114 113 CREATE OR REPLACE FUNCTION fn_total_client_bookings( … … 139 138 140 139 == Процедури == 141 Процедурите се користат за извршување на акции врз базата — за разлика од функциите, тие не враќаат вредност туку вршат промени. Секоја процедура содржи валидација пред да изврши каква било промена, со цел да се спречат невалидни состојби во базата.140 Процедурите се користат за извршување на акции врз базата — за разлика од функциите, тие не враќаат вредност туку вршат промени. Секоја процедура е одговорна за еден конкретен процес во системот. 142 141 143 142 === 1. sp_create_booking_request === 144 143 Процедурата {{{sp_create_booking_request}}} креира нов барање за booking од страна на клиент. 145 144 146 Пред да го изврши вметнувањето, процедурата врши три валидации: 147 * Проверува дали клиентот постои во табелата {{{ClientProfile}}} 148 * Проверува дали локацијата постои во табелата {{{Location}}} 149 * Проверува дали датумот на настанот е во иднина 150 151 Ако некоја од валидациите не помине, се фрла исклучок со јасна порака за грешка. 145 Прима ги сите потребни параметри — ID на клиент, траење на настанот, тип на настан, датум и локација — и ги вметнува во табелата {{{BookingRequest}}}. Преку {{{FOREIGN KEY}}} ограничувањата во базата се обезбедува дека само валидни вредности можат да бидат вметнати. 152 146 {{{ 153 147 CREATE OR REPLACE PROCEDURE sp_create_booking_request( … … 162 156 $$ 163 157 BEGIN 164 165 IF NOT EXISTS (166 SELECT 1 FROM ClientProfile WHERE client_id = p_client_id167 ) THEN168 RAISE EXCEPTION 'Client with ID % does not exist!', p_client_id;169 END IF;170 171 IF NOT EXISTS (172 SELECT 1 FROM Location WHERE location_id = p_location_id173 ) THEN174 RAISE EXCEPTION 'Location with ID % does not exist!', p_location_id;175 END IF;176 177 IF p_event_date <= CURRENT_DATE THEN178 RAISE EXCEPTION 'Event date must be in the future!';179 END IF;180 158 181 159 INSERT INTO BookingRequest( … … 201 179 Процедурата {{{sp_cancel_booking}}} го откажува постоечки booking со менување на неговиот статус во {{{CANCELLED}}}. 202 180 203 Пред да го изврши откажувањето, процедурата врши три проверки: 204 * Проверува дали booking со дадениот ID воопшто постои 205 * Проверува дали booking не е веќе откажан 206 * Проверува дали booking не е веќе завршен — завршените bookings не можат да се откажат 207 208 Без овие проверки, повикот со непостоечки ID би поминал тивко без никаква промена или грешка. 181 Прима го ID на booking-от и врши {{{UPDATE}}} врз табелата {{{Booking}}}. Со промената на статусот системот го евидентира откажувањето, а тригерот {{{trg_booking_status_history}}} автоматски ја запишува промената во историјатот. 209 182 {{{ 210 183 CREATE OR REPLACE PROCEDURE sp_cancel_booking( … … 214 187 AS 215 188 $$ 216 DECLARE 217 v_current_status VARCHAR(30); 218 BEGIN 219 220 SELECT booking_status 221 INTO v_current_status 222 FROM Booking 223 WHERE booking_id = p_booking_id; 224 225 IF NOT FOUND THEN 226 RAISE EXCEPTION 'Booking with ID % does not exist!', p_booking_id; 227 END IF; 228 229 IF v_current_status = 'CANCELLED' THEN 230 RAISE EXCEPTION 'Booking % is already cancelled!', p_booking_id; 231 END IF; 232 233 IF v_current_status = 'COMPLETED' THEN 234 RAISE EXCEPTION 'Booking % is already completed and cannot be cancelled!', p_booking_id; 235 END IF; 189 BEGIN 236 190 237 191 UPDATE Booking … … 246 200 Процедурата {{{sp_create_payment}}} креира ново плаќање поврзано со одреден booking. 247 201 248 Клучна поправка во однос на оригиналната верзија е дека плаќањето се креира со статус {{{PENDING}}} наместо веднаш {{{PAID}}}. Ова одговара на реалниот тек — плаќањето прво се иницира, па дури потоа се потврдува. 249 250 Процедурата врши три валидации: 251 * Проверува дали booking постои 252 * Проверува дали износот е позитивен број 253 * Проверува дали веќе не постои плаќање за истиот booking 202 Прима го ID на booking-от и износот на плаќањето и ги вметнува во табелата {{{Payment}}}. Плаќањето се креира со статус {{{PAID}}}, со што се евидентира успешно извршена трансакција. 254 203 {{{ 255 204 CREATE OR REPLACE PROCEDURE sp_create_payment( 256 205 p_booking_id INT, 257 p_amount NUMERIC(10,2)206 p_amount NUMERIC(10,2) 258 207 ) 259 208 LANGUAGE plpgsql … … 261 210 $$ 262 211 BEGIN 263 264 IF NOT EXISTS (265 SELECT 1 FROM Booking WHERE booking_id = p_booking_id266 ) THEN267 RAISE EXCEPTION 'Booking with ID % does not exist!', p_booking_id;268 END IF;269 270 IF p_amount <= 0 THEN271 RAISE EXCEPTION 'Payment amount must be greater than 0!';272 END IF;273 274 IF EXISTS (275 SELECT 1 FROM Payment WHERE booking_id = p_booking_id276 ) THEN277 RAISE EXCEPTION 'Payment for booking % already exists!', p_booking_id;278 END IF;279 212 280 213 INSERT INTO Payment( … … 286 219 p_booking_id, 287 220 p_amount, 288 'P ENDING'221 'PAID' 289 222 ); 290 223 … … 296 229 Тригерите се специјални функции кои се извршуваат автоматски при одредени настани во базата — без потреба апликацијата да ги повикува рачно. Во системот се имплементирани три тригери кои обезбедуваат конзистентност и интегритет на податоците. 297 230 298 === 1. trg_prevent_double_booking === 231 === 1. trg_update_slot_status === 232 Тригерот {{{trg_update_slot_status}}} автоматски го означува слободниот термин на артистот како {{{BOOKED}}} во моментот кога се креира нов booking. 233 234 Се извршува автоматски '''по''' секој нов запис во табелата {{{Booking}}} ({{{AFTER INSERT}}}). Го пронаоѓа соодветниот {{{AvailabilitySlot}}} според артистот и датумот на настанот преку subquery-и и го менува неговиот статус — со тоа се спречува двојна резервација на ист термин. 235 {{{ 236 CREATE OR REPLACE FUNCTION fn_update_slot_status() 237 RETURNS TRIGGER 238 AS 239 $$ 240 BEGIN 241 242 UPDATE AvailabilitySlot s 243 SET status = 'BOOKED' 244 WHERE s.bookable_id = ( 245 SELECT o.bookable_id 246 FROM Offer o 247 WHERE o.offer_id = NEW.offer_id 248 ) 249 AND DATE(s.start_datetime) = ( 250 SELECT br.event_date 251 FROM BookingRequest br 252 JOIN Offer o2 ON o2.request_id = br.request_id 253 WHERE o2.offer_id = NEW.offer_id 254 ); 255 256 RETURN NEW; 257 258 END; 259 $$ 260 LANGUAGE plpgsql; 261 262 CREATE OR REPLACE TRIGGER trg_update_slot_status 263 AFTER INSERT ON Booking 264 FOR EACH ROW 265 EXECUTE FUNCTION fn_update_slot_status(); 266 }}} 267 268 === 2. trg_prevent_double_booking === 299 269 Тригерот {{{trg_prevent_double_booking}}} спречува двоен booking — ситуација каде ист артист или бенд би бил потврден за два различни настани на ист датум. 300 270 301 Се извршува автоматски '''пред''' секој нов запис во табелата {{{Booking}}} ({{{BEFORE INSERT}}}). Доколку за истиот артист веќе постои {{{CONFIRMED}}} booking на истиот датум, тригерот фрла исклучок и го спречува вметнувањето.271 Се извршува автоматски '''пред''' секој нов запис во табелата {{{Booking}}} ({{{BEFORE INSERT}}}). Преку subquery-и го пронаоѓа артистот и датумот на новиот booking и проверува дали веќе постои {{{CONFIRMED}}} booking за истата комбинација. Доколку постои, тригерот фрла исклучок и го спречува вметнувањето. 302 272 {{{ 303 273 CREATE OR REPLACE FUNCTION fn_prevent_double_booking() … … 306 276 $$ 307 277 DECLARE 308 v_count INT; 309 v_bookable INT; 310 v_event_date DATE; 311 BEGIN 312 313 SELECT o.bookable_id, br.event_date 314 INTO v_bookable, v_event_date 315 FROM Offer o 316 JOIN BookingRequest br ON br.request_id = o.request_id 317 WHERE o.offer_id = NEW.offer_id; 278 v_count INT; 279 BEGIN 318 280 319 281 SELECT COUNT(*) … … 321 283 FROM Booking b 322 284 JOIN Offer o 323 ON b.offer_id = o.offer_id285 ON b.offer_id = o.offer_id 324 286 JOIN BookingRequest br 325 ON br.request_id = o.request_id 326 WHERE o.bookable_id = v_bookable 327 AND br.event_date = v_event_date 287 ON br.request_id = o.request_id 288 WHERE o.bookable_id = ( 289 SELECT bookable_id 290 FROM Offer 291 WHERE offer_id = NEW.offer_id 292 ) 293 AND br.event_date = ( 294 SELECT br2.event_date 295 FROM BookingRequest br2 296 JOIN Offer o2 ON o2.request_id = br2.request_id 297 WHERE o2.offer_id = NEW.offer_id 298 ) 328 299 AND b.booking_status = 'CONFIRMED'; 329 300 330 301 IF v_count > 0 THEN 331 RAISE EXCEPTION 'Artist/Band with ID % is already booked on %!', 332 v_bookable, v_event_date; 302 RAISE EXCEPTION 'Artist already booked on that date!'; 333 303 END IF; 334 304 … … 343 313 FOR EACH ROW 344 314 EXECUTE FUNCTION fn_prevent_double_booking(); 345 }}}346 347 === 2. trg_update_slot_status ===348 Тригерот {{{trg_update_slot_status}}} автоматски го означува слободниот термин на артистот како {{{BOOKED}}} во моментот кога се креира нов booking.349 350 Се извршува автоматски '''по''' секој нов запис во табелата {{{Booking}}} ({{{AFTER INSERT}}}). Го пронаоѓа соодветниот {{{AvailabilitySlot}}} според артистот и датумот на настанот и го менува неговиот статус — со тоа се спречува двојна резервација на ист термин.351 {{{352 CREATE OR REPLACE FUNCTION fn_update_slot_status()353 RETURNS TRIGGER354 AS355 $$356 DECLARE357 v_bookable INT;358 v_event_date DATE;359 BEGIN360 361 SELECT o.bookable_id, br.event_date362 INTO v_bookable, v_event_date363 FROM Offer o364 JOIN BookingRequest br ON br.request_id = o.request_id365 WHERE o.offer_id = NEW.offer_id;366 367 UPDATE AvailabilitySlot368 SET status = 'BOOKED'369 WHERE bookable_id = v_bookable370 AND DATE(start_datetime) = v_event_date371 AND status = 'AVAILABLE';372 373 RETURN NEW;374 375 END;376 $$377 LANGUAGE plpgsql;378 379 CREATE OR REPLACE TRIGGER trg_update_slot_status380 AFTER INSERT ON Booking381 FOR EACH ROW382 EXECUTE FUNCTION fn_update_slot_status();383 315 }}} 384 316
