Changes between Initial Version and Version 1 of функции,процедури,тригери


Ignore:
Timestamp:
05/25/26 16:36:16 (13 hours ago)
Author:
231233
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • функции,процедури,тригери

    v1 v1  
     1--------------Функции------------------------------
     2---------- Вкупна цена со попуст---------
     3CREATE OR REPLACE FUNCTION get_reservation_total_with_discount(
     4    p_reservation_id INT,
     5    p_promotion_id INT
     6)
     7RETURNS FLOAT AS $$
     8DECLARE
     9    v_total FLOAT;
     10    v_discount FLOAT;
     11    v_final FLOAT;
     12BEGIN
     13    -- Вкупна цена на сите тикети за резервацијата
     14    SELECT SUM(price)
     15    INTO v_total
     16    FROM TICKET
     17    WHERE reservation_id = p_reservation_id;
     18
     19    -- Попустот од промоцијата (во проценти)
     20    SELECT discount
     21    INTO v_discount
     22    FROM PROMOTION
     23    WHERE promotion_id = p_promotion_id;
     24
     25    -- Пресметај крајна цена
     26    v_final := v_total - (v_total * v_discount / 100);
     27
     28    RETURN v_final;
     29END;
     30$$ LANGUAGE plpgsql;
     31-----------------Слободни места-------------
     32CREATE OR REPLACE FUNCTION get_available_seats(
     33    p_screening_id INT
     34)
     35RETURNS INT AS $$
     36DECLARE
     37    v_capacity INT;
     38    v_sold INT;
     39BEGIN
     40    -- Капацитет на салата
     41    SELECT ch.capacity
     42    INTO v_capacity
     43    FROM SCREENING s
     44    JOIN CINEMA_HALL ch ON ch.cinemahall_id = s.cinemahall_id
     45    WHERE s.screening_id = p_screening_id;
     46
     47    -- Продадени тикети
     48    SELECT COUNT(*)
     49    INTO v_sold
     50    FROM TICKET
     51    WHERE screening_id = p_screening_id;
     52
     53    RETURN v_capacity - v_sold;
     54END;
     55$$ LANGUAGE plpgsql;
     56-------------------Просечен рејтинг--------------
     57CREATE OR REPLACE FUNCTION get_movie_avg_rating(
     58    p_movie_id INT
     59)
     60RETURNS FLOAT AS $$
     61DECLARE
     62    v_avg FLOAT;
     63BEGIN
     64    SELECT ROUND(AVG(rating)::numeric, 2)
     65    INTO v_avg
     66    FROM REVIEW
     67    WHERE movie_id = p_movie_id;
     68
     69    RETURN v_avg;
     70END;
     71$$ LANGUAGE plpgsql;
     72----------------Процедури--------------------
     73----------------Генерирање тикети за проекција  — ------------
     74CREATE OR REPLACE PROCEDURE generate_tickets_for_screening(
     75    p_screening_id INT,
     76    p_price FLOAT
     77)
     78LANGUAGE plpgsql AS $$
     79DECLARE
     80    v_cinemahall_id INT;
     81    v_cinema_id INT;
     82    v_seat RECORD;
     83    v_count INT := 0;
     84    v_screening_date DATE;
     85BEGIN
     86    -- Провери дали проекцијата постои
     87    IF NOT EXISTS (SELECT 1 FROM SCREENING WHERE screening_id = p_screening_id) THEN
     88        RAISE EXCEPTION 'Проекцијата со ID % не постои!', p_screening_id;
     89    END IF;
     90
     91    -- Провери дали датумот на проекцијата не е во минатото
     92    SELECT date INTO v_screening_date FROM SCREENING WHERE screening_id = p_screening_id;
     93    IF v_screening_date < CURRENT_DATE THEN
     94        RAISE EXCEPTION 'Не може да се генерираат тикети за минати проекции! Датум: %', v_screening_date;
     95    END IF;
     96
     97    -- Провери дали цената е валидна
     98    IF p_price <= 0 THEN
     99        RAISE EXCEPTION 'Цената мора да биде поголема од 0!';
     100    END IF;
     101
     102    -- Земи ја салата
     103    SELECT cinemahall_id INTO v_cinemahall_id
     104    FROM SCREENING WHERE screening_id = p_screening_id;
     105
     106    -- Провери дали салата постои
     107    IF NOT EXISTS (SELECT 1 FROM CINEMA_HALL WHERE cinemahall_id = v_cinemahall_id) THEN
     108        RAISE EXCEPTION 'Салата со ID % не постои!', v_cinemahall_id;
     109    END IF;
     110
     111    -- Провери дали салата е активна
     112    IF NOT EXISTS (SELECT 1 FROM CINEMA_HALL WHERE cinemahall_id = v_cinemahall_id AND status = 'ACTIVE') THEN
     113        RAISE EXCEPTION 'Салата со ID % не е активна!', v_cinemahall_id;
     114    END IF;
     115
     116    -- Провери дали киното постои
     117    SELECT cinema_id INTO v_cinema_id FROM CINEMA_HALL WHERE cinemahall_id = v_cinemahall_id;
     118    IF NOT EXISTS (SELECT 1 FROM CINEMA WHERE cinema_id = v_cinema_id) THEN
     119        RAISE EXCEPTION 'Киното со ID % не постои!', v_cinema_id;
     120    END IF;
     121
     122    -- Провери дали воопшто има седишта во салата
     123    IF NOT EXISTS (SELECT 1 FROM SEAT WHERE cinemahall_id = v_cinemahall_id) THEN
     124        RAISE EXCEPTION 'Салата со ID % нема седишта!', v_cinemahall_id;
     125    END IF;
     126
     127    -- За секое седиште во салата креирај тикет
     128    FOR v_seat IN
     129        SELECT seat_id FROM SEAT WHERE cinemahall_id = v_cinemahall_id
     130    LOOP
     131        IF NOT EXISTS (
     132            SELECT 1 FROM TICKET
     133            WHERE seat_id = v_seat.seat_id
     134            AND screening_id = p_screening_id
     135        ) THEN
     136            INSERT INTO TICKET (price, user_id, reservation_id, seat_id, screening_id)
     137            VALUES (p_price, NULL, NULL, v_seat.seat_id, p_screening_id);
     138            v_count := v_count + 1;
     139        END IF;
     140    END LOOP;
     141
     142    IF v_count = 0 THEN
     143        RAISE NOTICE 'Сите тикети за проекција % веќе се генерирани!', p_screening_id;
     144    ELSE
     145        RAISE NOTICE 'Генерирани % тикети за проекција % во сала % по цена %',
     146            v_count, p_screening_id, v_cinemahall_id, p_price;
     147    END IF;
     148END;
     149$$;
     150-------------------------Процедура 2 — Купување тикет со производи-----------------
     151CREATE OR REPLACE PROCEDURE purchase_ticket_with_products(
     152    p_ticket_id INT,
     153    p_user_id VARCHAR(13),
     154    p_reservation_id INT,
     155    p_products INT[]
     156)
     157LANGUAGE plpgsql AS $$
     158DECLARE
     159    v_product_id INT;
     160    v_price FLOAT;
     161    v_screening_id INT;
     162    v_screening_date DATE;
     163    v_total_price FLOAT := 0;
     164BEGIN
     165    -- Провери дали тикетот постои
     166    IF NOT EXISTS (SELECT 1 FROM TICKET WHERE ticket_id = p_ticket_id) THEN
     167        RAISE EXCEPTION 'Тикетот со ID % не постои!', p_ticket_id;
     168    END IF;
     169
     170    -- Провери дали тикетот е слободен
     171    IF EXISTS (SELECT 1 FROM TICKET WHERE ticket_id = p_ticket_id AND user_id IS NOT NULL) THEN
     172        RAISE EXCEPTION 'Тикетот со ID % е веќе купен!', p_ticket_id;
     173    END IF;
     174
     175    -- Провери дали корисникот постои
     176    IF NOT EXISTS (SELECT 1 FROM "USER" WHERE user_id = p_user_id) THEN
     177        RAISE EXCEPTION 'Корисникот % не постои!', p_user_id;
     178    END IF;
     179
     180    -- Провери дали резервацијата постои
     181    IF NOT EXISTS (SELECT 1 FROM RESERVATION WHERE reservation_id = p_reservation_id) THEN
     182        RAISE EXCEPTION 'Резервацијата со ID % не постои!', p_reservation_id;
     183    END IF;
     184
     185    -- Провери дали резервацијата не е откажана
     186    IF EXISTS (SELECT 1 FROM RESERVATION WHERE reservation_id = p_reservation_id AND status = 'CANCELLED') THEN
     187        RAISE EXCEPTION 'Резервацијата со ID % е откажана!', p_reservation_id;
     188    END IF;
     189
     190    -- Провери дали проекцијата не е во минатото
     191    SELECT screening_id INTO v_screening_id FROM TICKET WHERE ticket_id = p_ticket_id;
     192    SELECT date INTO v_screening_date FROM SCREENING WHERE screening_id = v_screening_id;
     193    IF v_screening_date < CURRENT_DATE THEN
     194        RAISE EXCEPTION 'Не може да се купи тикет за минати проекции! Датум: %', v_screening_date;
     195    END IF;
     196
     197    -- Провери дали производите постојат и пресметај вкупна цена
     198    FOREACH v_product_id IN ARRAY p_products
     199    LOOP
     200        IF NOT EXISTS (SELECT 1 FROM PRODUCT WHERE product_id = v_product_id) THEN
     201            RAISE EXCEPTION 'Производот со ID % не постои!', v_product_id;
     202        END IF;
     203        SELECT price INTO v_price FROM PRODUCT WHERE product_id = v_product_id;
     204        v_total_price := v_total_price + v_price;
     205    END LOOP;
     206
     207    -- Ажурирај го тикетот
     208    UPDATE TICKET
     209    SET user_id = p_user_id,
     210        reservation_id = p_reservation_id
     211    WHERE ticket_id = p_ticket_id;
     212
     213    -- Додај ги производите
     214    FOREACH v_product_id IN ARRAY p_products
     215    LOOP
     216        INSERT INTO PURCHASED_PRODUCT (numbers, ticket_id, product_id)
     217        VALUES (1, p_ticket_id, v_product_id);
     218    END LOOP;
     219
     220    RAISE NOTICE 'Тикетот % е успешно купен! Корисник: %, Производи: %, Вкупна цена на производи: %',
     221        p_ticket_id, p_user_id, array_length(p_products, 1), v_total_price;
     222
     223EXCEPTION
     224    WHEN OTHERS THEN
     225        RAISE NOTICE 'Грешка: % — се прави ROLLBACK!', SQLERRM;
     226        ROLLBACK;
     227        RETURN;
     228END;
     229$$;
     230----------------------------Процедура 3 — Додај review за филм--------------------
     231CREATE OR REPLACE PROCEDURE add_movie_review(
     232    p_user_id VARCHAR(13),
     233    p_movie_id INT,
     234    p_rating INT,
     235    p_comment VARCHAR(255)
     236)
     237LANGUAGE plpgsql AS $$
     238DECLARE
     239    v_existing_reviews INT;
     240    v_avg_rating FLOAT;
     241BEGIN
     242    -- Провери дали корисникот постои
     243    IF NOT EXISTS (SELECT 1 FROM "USER" WHERE user_id = p_user_id) THEN
     244        RAISE EXCEPTION 'Корисникот % не постои!', p_user_id;
     245    END IF;
     246
     247    -- Провери дали филмот постои
     248    IF NOT EXISTS (SELECT 1 FROM MOVIE WHERE movie_id = p_movie_id) THEN
     249        RAISE EXCEPTION 'Филмот со ID % не постои!', p_movie_id;
     250    END IF;
     251
     252    -- Провери дали рејтингот е валиден
     253    IF p_rating < 1 OR p_rating > 10 THEN
     254        RAISE EXCEPTION 'Рејтингот мора да биде помеѓу 1 и 10! Внесен рејтинг: %', p_rating;
     255    END IF;
     256
     257    -- Провери дали коментарот е празен
     258    IF p_comment IS NULL OR TRIM(p_comment) = '' THEN
     259        RAISE EXCEPTION 'Коментарот не може да биде празен!';
     260    END IF;
     261
     262    -- Провери дали корисникот купил тикет за овој филм
     263    IF NOT EXISTS (
     264        SELECT 1 FROM TICKET t
     265        JOIN SCREENING s ON s.screening_id = t.screening_id
     266        WHERE t.user_id = p_user_id
     267        AND s.movie_id = p_movie_id
     268    ) THEN
     269        RAISE EXCEPTION 'Корисникот % не купил тикет за филмот %!', p_user_id, p_movie_id;
     270    END IF;
     271
     272    -- Провери дали корисникот веќе оставил review за овој филм
     273    IF EXISTS (SELECT 1 FROM REVIEW WHERE user_id = p_user_id AND movie_id = p_movie_id) THEN
     274        RAISE EXCEPTION 'Корисникот % веќе оставил review за филмот %!', p_user_id, p_movie_id;
     275    END IF;
     276
     277    -- Додај review
     278    INSERT INTO REVIEW (comment, date, user_id, movie_id, rating)
     279    VALUES (p_comment, CURRENT_DATE, p_user_id, p_movie_id, p_rating);
     280
     281    -- Пресметај нов просечен рејтинг
     282    SELECT COUNT(*), ROUND(AVG(rating)::numeric, 2)
     283    INTO v_existing_reviews, v_avg_rating
     284    FROM REVIEW WHERE movie_id = p_movie_id;
     285
     286    RAISE NOTICE 'Review за филм % додаден! Рејтинг: %, Вкупно reviews: %, Просечен рејтинг: %',
     287        p_movie_id, p_rating, v_existing_reviews, v_avg_rating;
     288END;
     289$$;
     290
     291-----------------------Тригери------------------------------------
     292----------------Тригер 1 — Спречи двојно резервирање на исто седиште-----------
     293CREATE OR REPLACE FUNCTION check_seat_availability()
     294RETURNS TRIGGER AS $$
     295BEGIN
     296    IF EXISTS (
     297        SELECT 1 FROM TICKET
     298        WHERE seat_id = NEW.seat_id
     299        AND screening_id = NEW.screening_id
     300    ) THEN
     301        RAISE EXCEPTION 'Седиштето % е веќе зафатено за оваа проекција!', NEW.seat_id;
     302    END IF;
     303    RETURN NEW;
     304END;
     305$$ LANGUAGE plpgsql;
     306
     307CREATE OR REPLACE TRIGGER trg_check_seat_availability
     308BEFORE INSERT ON TICKET
     309FOR EACH ROW
     310EXECUTE FUNCTION check_seat_availability();
     311-----------------------Тригер 2 — Автоматска нотификација при резервација-----------
     312    CREATE OR REPLACE FUNCTION notify_on_reservation()
     313RETURNS TRIGGER AS $$
     314BEGIN
     315    INSERT INTO NOTIFICATION (message, date, user_id)
     316    VALUES (
     317        'Вашата резервација е успешно креирана со ID: ' || NEW.reservation_id,
     318        CURRENT_DATE,
     319        NEW.user_id
     320    );
     321    RETURN NEW;
     322END;
     323$$ LANGUAGE plpgsql;
     324
     325CREATE OR REPLACE TRIGGER trg_notify_on_reservation
     326AFTER INSERT ON USER_RESERVATION
     327FOR EACH ROW
     328EXECUTE FUNCTION notify_on_reservation();
     329-----------------------Тригер 3 — Автоматски COMPLETED статус----------------------
     330CREATE OR REPLACE FUNCTION complete_past_reservations()
     331RETURNS TRIGGER AS $$
     332BEGIN
     333    IF NEW.date < CURRENT_DATE THEN
     334        UPDATE RESERVATION r
     335        SET status = 'COMPLETED'
     336        FROM TICKET t
     337        WHERE t.reservation_id = r.reservation_id
     338        AND t.screening_id = NEW.screening_id
     339        AND r.status = 'CONFIRMED';
     340    END IF;
     341    RETURN NEW;
     342END;
     343$$ LANGUAGE plpgsql;
     344CREATE EXTENSION IF NOT EXISTS postgis;
     345
     346
     347
     348-- Тест процедура 1 — генерирај тикети
     349CALL generate_tickets_for_screening(55105026, 350.00);
     350
     351ALTER TABLE TICKET ALTER COLUMN user_id DROP NOT NULL;
     352ALTER TABLE TICKET ALTER COLUMN reservation_id DROP NOT NULL;
     353
     354SELECT column_name, is_nullable
     355FROM information_schema.columns
     356WHERE table_name = 'ticket'
     357AND column_name IN ('user_id', 'reservation_id');
     358
     359CALL generate_tickets_for_screening(55105026, 350.00);
     360
     361-- Земи слободен тикет од новата проекција
     362SELECT ticket_id FROM TICKET
     363WHERE screening_id = 55105026
     364AND user_id IS NULL
     365LIMIT 1;
     366
     367-- Земи вистински user_id
     368SELECT user_id FROM "USER" LIMIT 1;
     369
     370-- Земи вистински reservation_id со статус PENDING
     371SELECT reservation_id FROM RESERVATION
     372WHERE status = 'PENDING'
     373LIMIT 1;
     374
     375-- Земи вистински product_id
     376SELECT product_id FROM PRODUCT LIMIT 3;
     377
     378CALL purchase_ticket_with_products(12818005, 'admin01', 4000005, ARRAY[1, 2, 3]);
     379CALL add_movie_review('admin01', 195, 8, 'Одличен филм, препорачувам!');
     380
     381-- Земи филм за кој admin01 има тикет но нема review
     382SELECT DISTINCT s.movie_id
     383FROM TICKET t
     384JOIN SCREENING s ON s.screening_id = t.screening_id
     385WHERE t.user_id = 'admin01'
     386AND s.movie_id NOT IN (SELECT movie_id FROM REVIEW WHERE user_id = 'admin01')
     387LIMIT 1;
     388
     389SELECT DISTINCT s.movie_id
     390FROM TICKET t
     391JOIN SCREENING s ON s.screening_id = t.screening_id
     392WHERE t.user_id = 'admin01'
     393AND s.movie_id NOT IN (SELECT movie_id FROM REVIEW WHERE user_id = 'admin01')
     394LIMIT 1;
     395
     396SELECT DISTINCT s.movie_id
     397FROM TICKET t
     398JOIN SCREENING s ON s.screening_id = t.screening_id
     399WHERE t.user_id = 'admin01'
     400AND s.movie_id NOT IN (SELECT movie_id FROM REVIEW WHERE user_id = 'admin01')
     401LIMIT 1;
     402SELECT user_id FROM "USER" WHERE user_id != 'admin01' LIMIT 1;
     403
     404SELECT DISTINCT s.movie_id
     405FROM TICKET t
     406JOIN SCREENING s ON s.screening_id = t.screening_id
     407WHERE t.user_id = 'admin02'
     408AND s.movie_id NOT IN (SELECT movie_id FROM REVIEW WHERE user_id = 'admin02')
     409LIMIT 1;
     410CALL add_movie_review('admin02', 195, 7, 'Многу добар филм!');