| | 1 | == Индекси |
| | 2 | |
| | 3 | Индексите ги забрзуваат пребарувањата на често пребарувани колони и странски клучеви. |
| | 4 | {{{#!sql |
| | 5 | CREATE INDEX idx_user_email ON Hotel_User(email); |
| | 6 | CREATE INDEX idx_room_type_available ON Room(room_type, available); |
| | 7 | CREATE INDEX idx_reservation_customer ON Reservation(customer_id); |
| | 8 | CREATE INDEX idx_payment_reservation ON Payment(reservation_id); |
| | 9 | CREATE INDEX idx_service_staff ON Service(staff_id); |
| | 10 | }}} |
| | 11 | |
| | 12 | == Погледи |
| | 13 | |
| | 14 | === Вкупна цена на резервации |
| | 15 | {{{#!sql |
| | 16 | CREATE OR REPLACE VIEW reservation_total AS |
| | 17 | SELECT r.reservation_id, |
| | 18 | (EXTRACT(DAY FROM (r.end_date - r.start_date)) * rm.price_per_night) AS total_cost |
| | 19 | FROM Reservation r |
| | 20 | JOIN Room rm ON r.room_number = rm.room_number |
| | 21 | AND r.building_id = rm.building_id; |
| | 22 | }}} |
| | 23 | |
| | 24 | === Активни резервации по клиент |
| | 25 | {{{#!sql |
| | 26 | CREATE OR REPLACE VIEW customer_reservations AS |
| | 27 | SELECT c.user_id, u.first_name, u.last_name, r.reservation_id, |
| | 28 | r.start_date, r.end_date, r.status |
| | 29 | FROM Customer c |
| | 30 | JOIN Hotel_User u ON u.user_id = c.user_id |
| | 31 | JOIN Reservation r ON r.customer_id = c.user_id; |
| | 32 | }}} |
| | 33 | |
| | 34 | === Услуги обезбедени од персоналот |
| | 35 | {{{#!sql |
| | 36 | CREATE OR REPLACE VIEW staff_services AS |
| | 37 | SELECT s.service_id, s.service_type, s.service_date, s.service_status, |
| | 38 | st.user_id AS staff_id, u.first_name, u.last_name |
| | 39 | FROM Service s |
| | 40 | JOIN Staff st ON st.user_id = s.staff_id |
| | 41 | JOIN Hotel_User u ON u.user_id = st.user_id; |
| | 42 | }}} |
| | 43 | |
| | 44 | === Преглед за динамичко пресметување на вкупната цена на резервацијата |
| | 45 | {{{#!sql |
| | 46 | CREATE OR REPLACE VIEW reservation_total AS |
| | 47 | SELECT r.reservation_id, |
| | 48 | r.start_date, |
| | 49 | r.end_date, |
| | 50 | rm.price_per_night, |
| | 51 | (EXTRACT(DAY FROM (r.end_date - r.start_date)) * rm.price_per_night) AS total_cost |
| | 52 | FROM Reservation r |
| | 53 | JOIN Room rm ON r.room_number = rm.room_number |
| | 54 | AND r.building_id = rm.building_id; |
| | 55 | }}} |
| | 56 | |
| | 57 | == Процедури |
| | 58 | |
| | 59 | === Креирање резервација |
| | 60 | {{{#!sql |
| | 61 | CREATE OR REPLACE PROCEDURE make_reservation( |
| | 62 | IN p_customer_id BIGINT, |
| | 63 | IN p_room_number INT, |
| | 64 | IN p_building_id VARCHAR(10), |
| | 65 | IN p_start DATE, |
| | 66 | IN p_end DATE |
| | 67 | ) |
| | 68 | LANGUAGE plpgsql |
| | 69 | AS $$ |
| | 70 | BEGIN |
| | 71 | -- Prevent overlapping reservations |
| | 72 | IF EXISTS ( |
| | 73 | SELECT 1 FROM Reservation |
| | 74 | WHERE room_number = p_room_number |
| | 75 | AND building_id = p_building_id |
| | 76 | AND status = 'Approved' |
| | 77 | AND daterange(start_date, end_date, '[]') && daterange(p_start, p_end, '[]') |
| | 78 | ) THEN |
| | 79 | RAISE EXCEPTION 'Room is already reserved for these dates'; |
| | 80 | END IF; |
| | 81 | |
| | 82 | -- Insert reservation |
| | 83 | INSERT INTO Reservation (start_date, end_date, status, room_number, building_id, customer_id) |
| | 84 | VALUES (p_start, p_end, 'Pending', p_room_number, p_building_id, p_customer_id); |
| | 85 | END; |
| | 86 | $$; |
| | 87 | }}} |
| | 88 | |
| | 89 | === Одобри резервација |
| | 90 | {{{#!sql |
| | 91 | CREATE OR REPLACE PROCEDURE approve_reservation( |
| | 92 | IN p_reservation_id BIGINT, |
| | 93 | IN p_manager_id BIGINT |
| | 94 | ) |
| | 95 | LANGUAGE plpgsql |
| | 96 | AS $$ |
| | 97 | BEGIN |
| | 98 | UPDATE Reservation |
| | 99 | SET status = 'Approved', |
| | 100 | manager_id = p_manager_id |
| | 101 | WHERE reservation_id = p_reservation_id; |
| | 102 | END; |
| | 103 | $$; |
| | 104 | }}} |
| | 105 | |
| | 106 | === Регистрирај плаќање |
| | 107 | {{{#!sql |
| | 108 | CREATE OR REPLACE PROCEDURE make_payment( |
| | 109 | IN p_reservation_id BIGINT, |
| | 110 | IN p_method VARCHAR(20) |
| | 111 | ) |
| | 112 | LANGUAGE plpgsql |
| | 113 | AS $$ |
| | 114 | BEGIN |
| | 115 | INSERT INTO Payment (p_method, reservation_id) |
| | 116 | VALUES (p_method, p_reservation_id); |
| | 117 | END; |
| | 118 | $$; |
| | 119 | }}} |
| | 120 | |
| | 121 | == Функции |
| | 122 | |
| | 123 | === Ноќи помеѓу датуми |
| | 124 | {{{#!sql |
| | 125 | CREATE OR REPLACE FUNCTION nights_between(p_start DATE, p_end DATE) |
| | 126 | RETURNS INT LANGUAGE sql |
| | 127 | AS $$ |
| | 128 | SELECT EXTRACT(DAY FROM (p_end - p_start)); |
| | 129 | $$; |
| | 130 | }}} |
| | 131 | |
| | 132 | === Пресметка на трошокот за резервација |
| | 133 | {{{#!sql |
| | 134 | CREATE OR REPLACE FUNCTION reservation_cost(p_reservation_id BIGINT) |
| | 135 | RETURNS NUMERIC |
| | 136 | LANGUAGE plpgsql |
| | 137 | AS $$ |
| | 138 | DECLARE |
| | 139 | cost NUMERIC; |
| | 140 | BEGIN |
| | 141 | SELECT (EXTRACT(DAY FROM (r.end_date - r.start_date)) * rm.price_per_night) |
| | 142 | INTO cost |
| | 143 | FROM Reservation r |
| | 144 | JOIN Room rm ON rm.room_number = r.room_number |
| | 145 | AND rm.building_id = r.building_id |
| | 146 | WHERE r.reservation_id = p_reservation_id; |
| | 147 | |
| | 148 | RETURN cost; |
| | 149 | END; |
| | 150 | $$; |
| | 151 | }}} |
| | 152 | |
| | 153 | == Тригери |
| | 154 | |
| | 155 | === Автоматско ажурирање на достапноста на собата по одобрувањето |
| | 156 | {{{#!sql |
| | 157 | CREATE OR REPLACE FUNCTION update_room_availability() |
| | 158 | RETURNS TRIGGER LANGUAGE plpgsql |
| | 159 | AS $$ |
| | 160 | BEGIN |
| | 161 | IF NEW.status = 'Approved' THEN |
| | 162 | UPDATE Room |
| | 163 | SET available = FALSE |
| | 164 | WHERE room_number = NEW.room_number |
| | 165 | AND building_id = NEW.building_id; |
| | 166 | ELSIF NEW.status = 'Cancelled' THEN |
| | 167 | UPDATE Room |
| | 168 | SET available = TRUE |
| | 169 | WHERE room_number = NEW.room_number |
| | 170 | AND building_id = NEW.building_id; |
| | 171 | END IF; |
| | 172 | RETURN NEW; |
| | 173 | END; |
| | 174 | $$; |
| | 175 | |
| | 176 | CREATE TRIGGER trg_update_room_availability |
| | 177 | AFTER UPDATE OF status ON Reservation |
| | 178 | FOR EACH ROW |
| | 179 | EXECUTE FUNCTION update_room_availability(); |
| | 180 | }}} |
| | 181 | |
| | 182 | === Спречување на бришење корисници со активни резервации |
| | 183 | {{{#!sql |
| | 184 | CREATE OR REPLACE FUNCTION prevent_user_delete() |
| | 185 | RETURNS TRIGGER LANGUAGE plpgsql |
| | 186 | AS $$ |
| | 187 | BEGIN |
| | 188 | IF EXISTS (SELECT 1 FROM Reservation WHERE customer_id = OLD.user_id AND status <> 'Cancelled') THEN |
| | 189 | RAISE EXCEPTION 'Cannot delete user with active reservations'; |
| | 190 | END IF; |
| | 191 | RETURN OLD; |
| | 192 | END; |
| | 193 | $$; |
| | 194 | |
| | 195 | CREATE TRIGGER trg_prevent_user_delete |
| | 196 | BEFORE DELETE ON Hotel_User |
| | 197 | FOR EACH ROW |
| | 198 | EXECUTE FUNCTION prevent_user_delete(); |
| | 199 | }}} |