67 | | Од анализата се забележуваат: |
68 | | |
69 | | room_number е уникатен само во комбинација со building_id → слаб ентитет. |
70 | | |
71 | | Во Reservation нема експлицитно модел на врска со Customer, Manager и Room → треба релации за интегритет. |
72 | | |
73 | | Во Payment, amount е транзитивно зависно од Reservation (amount = цена * број на ноќи). |
74 | | |
75 | | Во Service, моментално нема детална историја (само статус). |
| 67 | Од функционалните зависности и шемата, можеме да идентификуваме дека релацијата R вклучува различни ентитети кои се поврзани преку надворешни клучеви и делумни зависности. За да го идентификуваме кандидатскиот примарен клуч за целата релација |
| 68 | R, мора да се осигураме дека: сите не-примарни атрибути се функционално зависни од кандидатскиот примарен клуч и кандидатскиот клуч мора да биде уникатен низ целата релација. |
| 69 | |
| 70 | Гледајќи ги функционалните зависности и врски, кандидатскиот примарен клуч за целата релација R може да се заклучи како: |
| 71 | |
| 72 | **{reservation_id, p_id, service_id, room_number, building_id}** |
| 73 | |
| 74 | - reservation_id: Уникатно идентификува резервација. |
| 75 | |
| 76 | - p_id: Уникатно идентификува плаќање. |
| 77 | |
| 78 | - service_id: Уникатно идентификува услуга. |
| 79 | |
| 80 | - room_number и building_id: Заедно, тие идентификуваат одредена соба во одредена зграда. |
| 81 | |
| 82 | Овој сет на атрибути треба да може уникатно да го идентификува секој запис во релацијата R, осигурувајќи дека нема редундантност и нема двосмисленост. |
80 | | Тековниот дизајн е во 1NF: атомски атрибути, нема повеќевредносни атрибути складирани како едно поле. |
| 87 | За да постигнеме 1NF, мора да се осигураме дека сите атрибути во секоја торка се атомски, што значи дека не постојат повеќевредносни атрибути или композитни атрибути. |
| 88 | |
| 89 | ===== Проблем во тековната шема: |
| 90 | |
| 91 | Композитни атрибути како address (кои може да имаат повеќе компоненти како што се улица, град итн.). |
| 92 | |
| 93 | ===== Решение: |
| 94 | |
| 95 | Разделување на повеќевредносните атрибути во посебни колони. |
| 96 | |
| 97 | ===== Применето 1NF: |
| 98 | |
| 99 | Адресата во Hotel_Building треба да се подели на атомски компоненти: улица, град и поштенски код. |
| 100 | |
| 101 | Табела Hotel_Building: |
| 102 | |
| 103 | building_id, street, city, zip_code, floors, num_rooms |
| 104 | |
| 105 | Сите атрибути во секоја табела ќе бидат атомски, што значи дека нема да постојат дополнителни сложени или повеќевредносни атрибути. |
84 | | Нема парцијални зависности (освен кај Room, каде клуч е составен (building_id, room_number)). |
| 109 | За да постигнеме 2NF, треба да отстраниме делумни зависности. Атрибутот што не е примарен мора да биде целосно функционално зависен од целиот примарен клуч, а не само од негов дел. |
| 110 | |
| 111 | ===== Проблем во тековната шема: |
| 112 | |
| 113 | Во Reservation, атрибутите како user_id, manager_id и room_number се делумно зависни само од дел од примарниот клуч (т.е. reservation_id), а не од целиот примарен клуч. |
| 114 | |
| 115 | ===== Решение: |
| 116 | |
| 117 | Преместување на атрибутите што зависат од дел од примарниот клуч во посебни табели за да ги елиминирате делумните зависности. |
| 118 | |
| 119 | ===== Применето 2NF: |
| 120 | |
| 121 | Креирање табела Customer со user_id како примарен клуч и атрибути поврзани со клиентот. |
| 122 | |
| 123 | Креирање табела Manager со user_id како примарен клуч и атрибути поврзани со менаџерот. |
| 124 | |
| 125 | Изменување на Reservation за да ги чува само атрибутите што зависат од reservation_id и преместување на user_id (клиент) и manager_id во нивните соодветни табели. |
| 126 | |
| 127 | По 2NF: |
| 128 | |
| 129 | Customer: user_id, first_name, last_name, phone, email, password |
| 130 | |
| 131 | Manager: user_id, first_name, last_name, phone, email, password |
| 132 | |
| 133 | Reservation: reservation_id, start_date, end_date, reserv_date, status, room_number, building_id, customer_id, manager_id |
88 | | Проблеми: |
89 | | |
90 | | Reservation – amount (треба да се изведува од Room.price_per_night × број на ноќи → не треба да стои во Payment како атрибут). |
91 | | |
92 | | Reservation моментално чува и customer_id, и manager_id → транзитивна зависност преку User. |
93 | | |
94 | | Подобрувања за да се задоволи 3 НФ |
95 | | Reservation – Order Cost |
96 | | |
97 | | Да не се чува amount во Payment, туку да се пресметува. |
98 | | |
99 | | {{{#!sql |
100 | | ALTER TABLE Payment DROP COLUMN amount; |
101 | | }}} |
102 | | Да се додава VIEW: |
103 | | {{{#!sql |
104 | | CREATE VIEW reservation_total AS |
105 | | SELECT r.reservation_id, |
106 | | (EXTRACT(DAY FROM r.end_date - r.start_date) * rm.price_per_night) AS total_cost |
107 | | FROM Reservation r |
108 | | JOIN Room rm ON r.room_number = rm.room_number AND r.building_id = rm.building_id; |
109 | | }}} |
110 | | Service – историја на статуси |
111 | | {{{#!sql |
112 | | CREATE TABLE service_history ( |
113 | | history_id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, |
114 | | service_id BIGINT NOT NULL, |
115 | | status VARCHAR(70) NOT NULL, |
116 | | changed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, |
117 | | FOREIGN KEY (service_id) REFERENCES Service(service_id) |
118 | | ); |
119 | | }}} |
| 137 | За да постигнеме 3NF, мора да ги отстраниме транзитивните зависности. Непримарниот атрибут не смее да зависи од друг непримарен атрибут. |
| 138 | |
| 139 | ===== Проблеми во тековната шема: |
| 140 | |
| 141 | Во Payment, user_id индиректно одредува атрибути како first_name и last_name, создавајќи транзитивна зависност. |
| 142 | |
| 143 | ===== Решение: |
| 144 | |
| 145 | Отстранување на транзитивните зависности со креирање посебни табели за User (веќе направено во 2NF) и осигурувајќи се дека секоја табела ги складира само атрибутите што директно зависат од примарниот клуч. |
| 146 | |
| 147 | ===== Применето 3NF: |
| 148 | |
| 149 | Во табелата Payment, складираме само p_id, p_method, amount, p_date и reservation_id. До user_id може да се пристапи преку reservation_id, па затоа не треба да се чува во табелата Payment. |
| 150 | |
| 151 | ===== По 3NF: |
| 152 | |
| 153 | Payment: p_id, p_method, amount, p_date, reservation_id |
| 154 | |
121 | | Прекршоци |
122 | | |
123 | | email во Hotel_User треба да е уникатен → веќе има UNIQUE. |
124 | | |
125 | | phone исто така треба да е уникатен (еден број = еден корисник). |
126 | | |
127 | | Во Room, комбинацијата (building_id, room_number) е единствен идентификатор. |
128 | | {{{#!sql |
129 | | ALTER TABLE Hotel_User ADD CONSTRAINT uq_phone UNIQUE(phone); |
130 | | |
131 | | ALTER TABLE Room ADD CONSTRAINT uq_room UNIQUE(building_id, room_number); |
132 | | }}} |
| 156 | |
| 157 | За да се постигне BCNF, мора да се осигураме дека секоја детерминанта во релацијата е клуч-кандидат, односно За секоја функционална зависност: A→B, A мора да биде клуч-кандидат. |
| 158 | |
| 159 | ===== Проблем во тековната шема: |
| 160 | |
| 161 | Во табелата Service, функционалната зависност: |
| 162 | |
| 163 | service_id → service_type, service_date, service_status, staff_id, room_number, building_id |
| 164 | |
| 165 | Оваа функционална зависност покажува дека service_id ги одредува service_type, service_date и service_status, но room_number и building_id не се дел од клучот-кандидат. Така, room_number и building_id се не-примарни атрибути кои зависат од service_id, создавајќи потенцијално прекршување на BCNF. |
| 166 | |
| 167 | ===== Решение за BCNF: |
| 168 | |
| 169 | |
| 170 | За да го решиме ова прекршување, треба да се осигураме дека секоја детерминанта е клуч-кандидат. |
| 171 | |
| 172 | room_number и building_id се одредени од композитниот клуч (room_number, building_id) во табелата Room, што е клуч-кандидат. |
| 173 | |
| 174 | Треба да ја отстраниме зависноста од room_number и building_id од табелата Service бидејќи овие атрибути се веќе одредени од табелата Room. |
| 175 | |
| 176 | ===== По примена на BCNF: |
| 177 | |
| 178 | Табелата Service ќе ги чува само: |
| 179 | |
| 180 | service_id, service_type, service_date, service_status и staff_id. |
| 181 | |
| 182 | Табелата Room ќе продолжи да ги чува деталите поврзани со собата, вклучувајќи го room_number и building_id. |
| 183 | |
| 184 | Со ова, сите не-примарни атрибути во табелата Service зависат единствено од клучот-кандидат service_id. |
| 185 | |
| 186 | ===== Конечни табели во BCNF: |
| 187 | |
| 188 | По примената на BCNF и решавањето на сите проблеми, конечната шема за засегнатите табели изгледа вака: |
| 189 | |
| 190 | Hotel_Bidling: building_id, street, city, zip_code, floors, num_rooms |
| 191 | |
| 192 | Room: room_number, building_id, room_type, number_of_beds, price_per_night, available |
| 193 | |
| 194 | Customer: user_id, first_name, last_name, phone, e-mail, password |
| 195 | |
| 196 | Manager: user_id, first_name, last_name, phone, e-mail, password |
| 197 | |
| 198 | Reservation: reservation_id, start_date, end_date, reserv_date, status, customer_id, manager_id, room_number, building_id |
| 199 | |
| 200 | Payment: p_id, p_method, amount, p_date, reservation_id |
| 201 | |
| 202 | Service: service_id, service_type, service_date, service_status, staff_id |