= Нормализација За табелите од нашата база на податоци, со цел разликување на самите id за секој ентитет, ќе ги преименуваме id атрибутите во **<име_на_релација>_id**. Со тоа добиваме: **user_id, non_admin_user_id, admin_id, listener_id, musical_entity_id, song_id, artist_id, event_id, album_id, playlist_id**. == Функционални зависности: - FD1: user_id -> profile_photo, email, username, full_name, password - FD3: musical_entity_id -> title, genre, me_cover, release_date, artist_id - FD4: song_id -> link, album_id - FD5: playlist_id -> playlist_cover, playlist_name, listener_id - FD6: event_id -> event_name, location, venue, date, time, user_id - FD7: (artist_id, musical_entity_id) -> role - FD8: (listener_id, musical_entity_id) -> grade, comment - FD9: non_admin_user_id -> user_id - FD10: admin_id -> user_id - FD11: listener_id -> non_admin_user_id - FD12: artist_id -> non_admin_user_id - FD13: album_id -> musical_entity_id - FD14: (listener_id,song_id,timestamp) -> / **Лево**: playlist_id, event_id, song_id, timestamp, admin_id **Десно**: profile_photo, email, username, full_name, password, title, genre, release_date, me_cover, link, playlist_cover, playlist_name, event_name, location, venue, date, time, role, grade, comment **Лево и десно**: user_id, musical_entity_id, artist_id, non_admin_user_id, album_id, listener_id === Глобална релација R={ playlist_id, event_id, song_id, timestamp, admin_id, profile_photo, email, username, full_name, password, title, genre, release_date, me_cover, link, playlist_cover, playlist_name, event_name, location, venue, date, time, role, grade, comment, user_id, musical_entity_id, artist_id, non_admin_user_id, album_id, listener_id} == Покривачи 1. **playlist_id+** = {playlist_id, playlist_name, playlist_cover, listener_id, user_id, full_name, email, password, username, profile_photo} -> **Не ги содржи сите атрибути** 2. **event_id+** = {event_id, event_name, location, venue, date, time, user_id, full_name, email, password, username, profile_photo} -> **Не ги содржи сите атрибути** 3. **song_id+** = {song_id, link, album_id, musical_entity_id, title, genre, me_cover, release_date, artist_id, non_admin_user_id, user_id, full_name, email, password, username, profile_photo} -> **Не ги содржи сите атрибути** 4. **timestamp+** = {timestamp} -> **Не ги содржи сите атрибути** 5. **admin_id+** = {admin_id, user_id, full_name, email, password, username, profile_photo } -> **Не ги содржи сите атрибути** == Спојување Покривачи Бидејќи атрибутите кои се наоѓаат само на левата страна не може да се изведат на никој друг начин, тие мора да бидат дел од примарниот клуч. **{playlist_id, event_id, song_id, timestamp, admin_id}+** = { playlist_id, event_id, song_id, timestamp, admin_id, playlist_name, playlist_cover, user_id, listener_id, full_name, email, password, username, profile_photo, event_name, location, venue, date, time, link, album_id, musical_entity_id, title, genre, me_cover, release_date, artist_id, non_admin_user_id, role, grade, comment} Покривачот составен од сите атрибути кои се наоѓаат само од левата страна ги содржи сите атрибути на глобалната релација. Всушност тоа ни е и минимален суперклуч. Во нашиот случај ова ни е единствениот кандидат клуч, па оттаму тоа ни е и примарниот клуч на релацијата. Избран примарен клуч: **{playlist_id, event_id, song_id, timestamp, admin_id}** == Проверка за 1НФ Бидејќи релацијата не содржи повеќевредносни атрибути, неуникатни атрибути, вредности од различен домен за еден атрибут, а притоа секоја редица е уникатно определена преку примарниот клуч, **ја задоволува 1НФ**. == Проверка за 2НФ R **не ја задоволува 2НФ** поради постоење на парцијални зависности, како на пример `playlist_name, playlist_cover, listener_id` кои зависат само од `playlist_id`, а не од целиот примарен клуч. == Декомпозиција во 2НФ Најпрво ги групираме атрибутите само според клучевите од кои зависат. - playlist_id -> playlist_name, playlist_cover, listener_id - song_id -> link, album_id - event_id -> event_name, location, venue, date, time, user_id - admin_id -> user_id == Декомпозиција во релации 1. **Playlists(playlist_id, playlist_name, playlist_cover, listener_id)** R1 = R - {playlist_name, playlist_cover} R1 = { playlist_id, event_id, song_id, timestamp, admin_id, user_id, listener_id, full_name, email, password, username, profile_photo, event_name, location, venue, date, time, link, album_id, musical_entity_id, title, genre, me_cover, release_date, artist_id, non_admin_user_id, role, grade, comment} Lossless join: Релацијата може да се реконструира преку join со `playlist_id`. Dependency preservation: FD5 е сочувана преку новата релација `Playlists`. 2. **Songs(song_id, link, album_id)** R2 = R1 - {link} R2 = { playlist_id, event_id, song_id, timestamp, admin_id, user_id, listener_id, album_id, full_name, email, password, username, profile_photo, event_name, location, venue, date, time,musical_entity_id, title, genre, me_cover, release_date, artist_id,non_admin_user_id, role, grade, comment} Lossless join: Релацијата може да се реконструира преку join со `song_id` Dependency preservation: FD4 е сочувана преку новата релација `Songs` 3. **Events(event_id, event_name, location, venue, date, time, user_id)** R3 = R2 - {event_name, location, venue, date, time} R3 = {playlist_id, event_id, song_id, timestamp, admin_id, user_id, listener_id, album_id, full_name, email, password, username, profile_photo, musical_entity_id, title, genre, me_cover, release_date, artist_id, non_admin_user_id, role, grade, comment} Lossless join: Релацијата може да се реконструира преку join со `event_id` Dependency preservation: FD6 е сочувана преку новата релација `Events` 4. **Admins(admin_id, user_id)** Lossless join: Релацијата може да се реконструира преку join со `admin_id` Dependency preservation: FD10 е сочувана преку новата релација `Admins` Добиени релации: 1. Playlists(playlist_id, playlist_name, playlist_cover, listener_id) 2. Songs(song_id, link, album_id) 3. Events(event_id, event_name, location, venue, date, time, user_id) 4. Admins(admin_id, user_id) 5. R3 = { playlist_id, event_id, song_id, timestamp, admin_id, user_id, listener_id, album_id, full_name, email, password, username, profile_photo, musical_entity_id, title, genre, me_cover, release_date, artist_id, non_admin_user_id, role, grade, comment } == Проверка за 3НФ Поради постоењето на транзитивни зависности во добиените релации, можеме да заклучиме дека **е нарушена 3НФ**. На пример: `Playlists(playlist_id, playlist_name, playlist_cover, listener_id)` има транзитивна зависност преку `FD11: listener_id -> non_admin_user_id` == Декомпозиција по 3НФ 1. `Playlists(playlist_id, playlist_name, playlist_cover, listener_id)` -> има транзитивна зависност преку `FD11: listener_id -> non_admin_user_id` Декомпозиција по FD11: **Listeners(listener_id, non_admin_user_id)** -> има транзитивна зависност преку `FD9: non_admin_user_id -> user_id` Декомпозиција по FD9: **Non_Admin_Users(non_admin_user_id, user_id)** **Listeners(listener_id)** - `listener_id` е всушност примарен клуч во релацијата `Listeners` и надворешен клуч во релацијата `Non_Admin_Users`; `Non_Admin_Users(non_admin_user_id, user_id)` -> има транзитивна зависност преку `FD1: user_id -> profile_photo, email, username, full_name, password` Декомпозиција по FD1: **Users(user_id, profile_photo, email, username, full_name, password)** `Non_Admin_Users(non_admin_user_id)` - `non_admin_user_id` е всушност примарен клуч во релацијата `Non_Admin_Users` и надворешен клуч во релацијата `Users` R4 = R3 - {profile_photo, email, username, full_name, password} R4 = { playlist_id, event_id, song_id, timestamp, admin_id, user_id, listener_id, album_id, musical_entity_id, title, genre, me_cover, release_date, artist_id, non_admin_user_id, role, grade, comment} Lossless join: Релацијата може да се реконструира преку join со `user_id` Dependency preservation: FD1 е сочувана преку новата релација `Users` 2. `Songs(song_id, link, album_id)` -> има транзитивна зависност преку `FD13: album_id -> musical_entity_id` Декомпозиција по FD13: **Albums(album_id, musical_entity_id)** -> има транзитивна преку `FD3: musical_entity_id-> title, genre, me_cover, release_date, artist_id` Lossless join: Релацијата може да се реконструира преку join со `album_id` Dependency preservation: FD13 е сочувана преку новата релација `Albums` Декомпозиција по FD3: **Musical_Entities(musical_entity_id, title, genre, me_cover, release_date, artist_id)** `Albums(album_id)` - `album_id` е всушност примарен клуч во релацијата `Albums` и надворешен клуч во релацијата `Musical_Entities`. R5 = R4 - {title, genre, me_cover, release_date} R5 = { playlist_id, event_id, song_id, timestamp, admin_id,user_id, listener_id, album_id, musical_entity_id, artist_id, non_admin_user_id, role, grade, comment} Lossless join: Релацијата може да се реконструира преку join со `musical_entity_id` Dependency preservation: FD3 е сочувана преку новата релација `Musical_Entities` `Musical_Entities(musical_entity_id, title, genre, me_cover, release_date, artist_id)` -> има транзитивна зависност преку `FD12: artist_id -> non_admin_user_id` Декомпозиција по FD12: **Artists(artist_id)** - `artist_id` е всушност примарен клуч во релацијата `Artists` и надворешен клуч во релацијата `Non_Admin_Users`, каде е надворешен клуч во релацијата `Users`. Сите останати релации се добиени преку претходните декомпозиции. 3. `Events(event_id, event_name, location, venue, date, time, user_id)` -> има транзитивна зависност преку FD1, но веќе направивме декомпозиција по FD1. 4. `Admins(admin_id, user_id)` -> има транзитивна зависност преку FD1 и бидејќи `admin_id` е примарен клуч во `Admins` и надворешен клуч во `Users`, ја реформираме релацијата: **Admins(admin_id)**, каде `admin_id` е надворешен клуч во табелата `Users`. Добиени релации: 1. Playlists(playlist_id, playlist_name, playlist_cover, listener_id) 2. Songs(song_id, link, album_id) 3. Events(event_id, event_name, location, venue, date, time, user_id) 4. Admins(admin_id) 5. Listeners(listener_id) 6. Non_Admin_Users(non_admin_user_id) 7. Users(user_id, profile_photo, email, username, full_name, password) 8. Albums(album_id) 9. Musical_Entities(musical_entity_id, title, genre, me_cover, release_date, artist_id) 10. Artists(artist_id) 11. R5 = { playlist_id, event_id, song_id, timestamp, admin_id, user_id, listener_id, album_id, musical_entity_id, artist_id, non_admin_user_id, role, grade, comment} 5. Бидејќи `role` е определен од `(artist_id, musical_entity_id)` -> имаме транзитивна зависност преку `FD7:(artist_id, musical_entity_id) -> role` Декомпозиција по FD7: **Artist_Contributions(artist_id, musical_entity_id, role)** R6 = R5 - {role} R6 = { playlist_id, event_id, song_id, timestamp, admin_id, user_id, listener_id, album_id, musical_entity_id, artist_id, non_admin_user_id, grade, comment} Lossless join: Релацијата може да се реконструира преку join со `(artist_id, musical_entity_id)` Dependency preservation: FD7 е сочувана преку новата релација `Musical_Entities` 6. Бидејќи `grade`, `comment` се определени од `(listener_id, musical_entity_id)` -> имаме транзитивна зависност преку `FD8: (listener_id, musical_entity_id) -> grade, comment` Декомпозиција по FD8: **Reviews(listener_id, musical_entity_id, grade, comment)** R7 = R6 - {grade, comment} R7 = { playlist_id, event_id, song_id, timestamp, admin_id, user_id, listener_id, album_id, musical_entity_id, artist_id, non_admin_user_id} Lossless join: Релацијата може да се реконструира преку join со `(listener_id, musical_entity_id)`. Dependency preservation: FD8 е сочувана преку новата релација `Reviews`. == Проверка за БКНФ: Примарните клучеви се болдирани. 1. Playlists(**playlist_id**, playlist_name, playlist_cover, listener_id) -> **задоволува БКНФ** 2. Songs(**song_id**, link, album_id) -> **задоволува БКНФ** 3. Events(**event_id**, event_name, location, venue, date, time, user_id) -> **задоволува БКНФ** 4. Admins(**admin_id**) -> **задоволува БКНФ** 5. Listeners(**listener_id**) -> **задоволува БКНФ** 6. Non_Admin_Users(**non_admin_user_id**) -> **задоволува БКНФ** 7. Users(**user_id**, profile_photo, email, username, full_name, password) -> **задоволува БКНФ** 8. Albums(**album_id**) -> **задоволува БКНФ** 9. Musical_Entities(**musical_entity_id**, title, genre, me_cover, release_date, artist_id) -> **задоволува БКНФ** 10. Artist_Contributions(**artist_id, musical_entity_id**, role) -> **задоволува БКНФ** 11. Reviews(**listener_id, musical_entity_id**, grade, comment) -> **задоволува БКНФ** 12. Artists(**artist_id**) -> **задоволува БКНФ** 13. R7 = { **playlist_id, event_id, song_id, timestamp, admin_id, user_id, listener_id, album_id, musical_entity_id, artist_id, non_admin_user_id**} -> **задоволува БКНФ** == Проверка за 4НФ Релацијата R7 **не ја задоволува 4НФ** бидејќи содржи мултивредносни зависности, како на пример `listener_id →→ song_id, timestamp`, што значи дека слушањата на песните се целосно независни од лајковите на песните. == Декомпозиција по 4Нф: - **Playlist_Songs (song_id, playlist_id)** - **Performs_At (event_id, artist_id)** - **Likes (listener_id, musical_entity_id) - **Follows (follower_id, followee_id)**- во оваа релација `follower_id` и `followee_id` се всушност сложен примарен клуч и двете од нив се надворешни клучеви во релацијата `Non_Admin_Users`. - **Saved_Playlists (listener_id, playlist_id)** - **Listens (listener_id, song_id, timestamp)** Lossless join: Доколку направиме join по заедничките атрибути на овие релации можеме да ја реконстроуираме R7. Dependency preservation: Новодобиените релации не претставуваат функционални зависности. Финални релации: 1. Playlists(**playlist_id**, playlist_name, playlist_cover, listener_id) 2. Songs(**song_id**, link, album_id) 3. Events(**event_id**, event_name, location, venue, date, time, user_id) 4. Admins(**admin_id**) 5. Listeners(**listener_id**) 6. Non_Admin_Users(**non_admin_user_id**) 7. Users(**user_id**, profile_photo, email, username, full_name, password) 8. Albums(**album_id**) 9. Musical_Entities(**musical_entity_id**, title, genre, me_cover, release_date, artist_id) 10. Artist_Contributions(**artist_id, musical_entity_id**, role) 11. Reviews(**listener_id, musical_entity_id**, grade, comment) 12. Artists(**artist_id**) 13. Playlist_Songs(**song_id, playlist_id**) 14. Performs_At (**event_id, artist_id**) 15. Likes(**listener_id, musical_entity_id**) 16. Follows(**follower_id, followee_id**) 17. Saved_Playlists (**listener_id, playlist_id**) 18. Listens (**listener_id, song_id, timestamp**) == Заклучок При нормализацијата ги добивме истите релации од Фаза 2. Ова значи дека базата била соодветно моделирана.