== Нормализација на база и подобрување на дизајн === Идентификувани проблеми-атрибути Повеќевредносни (multivalued): * destination.types_of_places * destination.recommended_season * meteorological_condition.weather_condition (ако може да има >1 состојба) * meteorological_condition.warnings (можни повеќе warnings) * activity.category (ако активноста може да има >1 категорија) * preference.type_preference (ако корисникот може да има повеќе типови во една преференца) * event.event_type (ако е повеќевредносно) * users.phone_number (многу броеви по корисник) Сложени (composite / structured): * destination.important_location_name + destination.important_location_description → important_locations (повеќе знаменитости по дестинација) * loginName (во ЕР документација е сложен, но во реализација се користи users.first_name/last_name - нема посебна табела) * package.duration — веќе разделен како start_date / end_date ==== Справување со идентификувани проблеми За секој multivalued атрибут: се креира associative (linking) table со две колони (id_parent, value). Ако вредностите се повторливи низ записи (naming taxonomy) — се креира референтна табела (категории/типови) + many-to-many. Така имаме единствен извор на вистината (lookup). {{{ CREATE TABLE IF NOT EXISTS destination_place_type ( id_destination INT NOT NULL REFERENCES travel_sage.destination(id_destination) ON DELETE CASCADE, place_type VARCHAR(100) NOT NULL, PRIMARY KEY (id_destination, place_type) ); CREATE TABLE IF NOT EXISTS destination_season ( id_destination INT NOT NULL REFERENCES travel_sage.destination(id_destination) ON DELETE CASCADE, season VARCHAR(50) NOT NULL, PRIMARY KEY (id_destination, season) ); CREATE TABLE IF NOT EXISTS user_phone ( id_user INT NOT NULL REFERENCES travel_sage.users(id_user) ON DELETE CASCADE, phone_number VARCHAR(30) NOT NULL, PRIMARY KEY (id_user, phone_number) ); CREATE TABLE IF NOT EXISTS activity_category ( id_activity INT NOT NULL REFERENCES travel_sage.activity(id_activity) ON DELETE CASCADE, category VARCHAR(100) NOT NULL, PRIMARY KEY (id_activity, category) ); CREATE TABLE IF NOT EXISTS event_type ( id_event INT NOT NULL REFERENCES travel_sage.event(id_event) ON DELETE CASCADE, event_type VARCHAR(100) NOT NULL, PRIMARY KEY (id_event, event_type) ); CREATE TABLE IF NOT EXISTS preference_type ( id_preference INT NOT NULL REFERENCES travel_sage.preference(id_preference) ON DELETE CASCADE, pref_type VARCHAR(100) NOT NULL, PRIMARY KEY (id_preference, pref_type) ); CREATE TABLE IF NOT EXISTS meteo_condition_value ( id_meteo INT NOT NULL REFERENCES travel_sage.meteorological_condition(id_meteo) ON DELETE CASCADE, condition_value VARCHAR(100) NOT NULL, PRIMARY KEY (id_meteo, condition_value) ); CREATE TABLE IF NOT EXISTS meteo_warning_value ( id_meteo INT NOT NULL REFERENCES travel_sage.meteorological_condition(id_meteo) ON DELETE CASCADE, warning_text VARCHAR(255) NOT NULL, PRIMARY KEY (id_meteo, warning_text) ); CREATE TABLE IF NOT EXISTS important_location ( id_location SERIAL PRIMARY KEY, id_destination INT NOT NULL REFERENCES travel_sage.destination(id_destination) ON DELETE CASCADE, name VARCHAR(255) NOT NULL, description TEXT ); }}} === Функциски зависности и нормализација Со цел да се постигне функционален и скалабилен дизајн на базата на податоци за веб-апликацијата TravelSage, започнав со обединета релација која ги содржи сите атрибути од моделот. Оваа релација служи како почетна точка за анализа на функционалните зависности. Врз основа на таа анализа, се применува нормализација до третата нормална форма (3НФ), при што секој чекор вклучува логичка декомпозиција на релациите со цел да се избегнат вишоци, аномалии при ажурирање и несоодветни зависности. Бидејќи табелата на сите атрибути би била преголема, ја поделив по функционалност на апликацијата: * Менаџирање на дестинации и тагови * Менаџирање на корисници и нивни активности (рецензии, преференци, телефони) * Менаџирање на патувања (пакети, активности, резервации) * Менаџирање на настани и временски услови * Менаџирање на метеоролошки податоци === 1. Менаџирање на дестинации и тагови Почетна релација: R = { id_destination, location_name, location_desc, types_of_places, recommended_season, average_temp, latitude, longitude, country, popularity, important_location_name, important_location_description, id_tag, tag_name } Функциски зависности: * id_destination → сите атрибути што се однесуваат на една конкретна дестинација * id_tag → tag_name Објаснување: Секој ред во оваа релација ја претставува комбинацијата од дестинација и нејзиниот таг. Една дестинација може да има повеќе тагови, а еден таг може да се користи кај повеќе дестинации → многу-на-многу релација. Слично, types_of_places и recommended_season се повеќевредносни атрибути и мораат да се одвоени. important_location_name + important_location_description се сложени атрибути, па се декомпонирани во посебна табела. Нормализација: * 1НФ: Сите атрибути се атомарни (поделени се). * 2НФ: Податоците за тагови и повеќевредносни атрибути се одвоени во посебни табли, зависни од PK. * 3НФ: Нема транзитивни зависности; сите non-prime атрибути зависат само од PK. Декомпозиција: R1 = { id_destination, location_name, location_desc, average_temp, latitude, longitude, country, popularity } R2 = { id_tag, tag_name } R3 = { id_destination, id_tag } → destination_has_tags R4 = { id_destination, place_type } → destination_place_type R5 = { id_destination, season } → destination_season R6 = { id_location, id_destination, name, description } → important_location === 2. Менаџирање на корисници и нивни активности Почетна релација: R = { id_user, first_name, last_name, email, phone_number, birth_date, is_premium, id_review, id_reservation, username, quality, comment, review_date, vote_count, id_preference, type_preference, priority } Функциски зависности: * id_user → сите лични податоци * id_user, id_destination → рецензии и резервации * id_user → преференци * id_user → повеќе телефонски броеви Објаснување: Еден корисник може да има повеќе рецензии, преференци и телефонски броеви. Секој non-prime атрибут мора да зависи од целосен PK на релацијата, а не од дел од него. Повеќевредносните атрибути се одвоени во посебни табли. Нормализација: * 1НФ: Атомарни вредности (phone_number, type_preference распарсани). * 2НФ: Рецензии, преференци и телефони се одвоени во посебни табли за да нема делумни FD. * 3НФ: Нема транзитивни зависимости; username се елиминира и се користи reservation → user. Декомпозиција: R7 = { id_user, first_name, last_name, email, birth_date, is_premium } R8 = { id_user, phone_number } → user_phone R9 = { id_review, id_user, id_destination, quality, comment, review_date, vote_count } → review R10 = { id_preference, id_user, priority } R11 = { id_preference, type_preference } → preference_type === 3. Менаџирање на патувања, активности и резервации Почетна релација: R = { id_package, package_name, price, start_date, end_date, id_destination, id_activity, activity_name, information, category, amount, id_reservation, id_user, time_point, premium_discount_applied, discount_amount, total_price } Функциски зависности: * id_package → основни податоци за пакет * id_activity → опис на активноста * id_reservation → информации за резервацијата * id_activity, id_package → активности во пакет Објаснување: Многу-на-многу релација меѓу пакети и активности. Резервации се однесуваат на пакети или активности. category е повеќевредносен атрибут → одвоен. Нормализација: * 1НФ: Атомарни вредности. * 2НФ: Резервации и активности се одвоени. * 3НФ: Нема транзитивни зависимости; PK е секогаш јасен. Декомпозиција: R12 = { id_package, package_name, price, start_date, end_date, id_destination } R13 = { id_activity, activity_name, information, amount, id_destination } R14 = { id_package, id_activity } → package_activity R15 = { id_reservation, id_user, id_package, time_point, premium_discount_applied, discount_amount, total_price } R16 = { id_reservation, id_activity } → activity_reservation === 4. Менаџирање на настани и метеоролошки услови Почетна релација: R = { id_event, event_name, event_type, start_date, end_date, details, id_destination, id_meteo, current_temp, weather_condition, warnings, humidity, wind, month } Функциски зависности: * id_event → сите атрибути за настан * id_meteo → временски параметри (weather_condition и warnings се мулти) Објаснување: Секој настан е поврзан со дестинација и метео податоци. Повеќевредносните атрибути weather_condition и warnings се декомпонирани во посебни табли. Нормализација: * 1НФ: Атомарни вредности (мулти атрибути корегирани). * 2НФ: Метео податоците се во посебна релација. * 3НФ: Нема транзитивни врски. Декомпозиција: R17 = { id_event, event_name, start_date, end_date, details, id_destination } R18 = { id_event, event_type } → event_type R19 = { id_meteo, id_destination, current_temp, humidity, wind, month } → meteorological_condition R20 = { id_meteo, condition_value } → meteo_condition_value R21 = { id_meteo, warning_text } → meteo_warning_value === Кратко резиме * 1NF: Сите релации се атомарни, без list или вгнездени структури. * 2NF: Секој non-prime атрибут зависи од целиот PK; делумни зависности се елиминирани. * 3NF: Нема транзитивни зависимости. Lossless & Dependency‑preserving decomposition: * Декомпозициите се lossless, бидејќи пресеците секогаш вклучуваат PK, а сите FD се зачувани. Придобивки од нормализација: *Елиминација на вишоци и дупликации * Отстранување на аномалии при внесување, бришење или ажурирање * Подобра логичка организација на податоците * Јасна поделба според функционалност (модули) * Можност за идно проширување на моделот без пречки На овој начин, дизајнот на базата ги исполнува тековните барања на апликацијата TravelSage и обезбедува стабилна основа за идно проширување и еволуција.