| | 1 | = Нормализација = |
| | 2 | |
| | 3 | == Денормализирана форма на базата == |
| | 4 | |
| | 5 | Тргнуваме од една унифицирана релација R која ги содржи сите атрибути од ЕР моделот. Бидејќи имињата на атрибутите мора да бидат единствени, атрибутите се именувани со префикс според ентитетот. |
| | 6 | |
| | 7 | Работниците и администраторите учествуваат во повеќе релации со различни улоги (работник кој менува статус, кој пишува коментар и кој е доделен; администратор кој управува со категорија и кој доделува пријава). Една иста вредност не може да ги опише сите улоги во еден ред, па за секоја улога се воведува посебен атрибут (log_worker_id, comment_worker_id, assignment_worker_id, category_admin_id, assignment_admin_id). Сопствените атрибути на работникот и администраторот (worker_id, admin_id и нивните податоци) остануваат посебни. Граѓанинот и категоријата учествуваат само во една релација, па citizen_id и category_id се исти атрибути и во ентитетот и во пријавата. Сите фотографии, статуси, коментари и доделувања во еден ред се однесуваат на иста пријава, па report_id е еден атрибут. |
| | 8 | |
| | 9 | {{{ |
| | 10 | R(admin_id, admin_full_name, admin_email, admin_password, |
| | 11 | worker_id, worker_full_name, worker_email, worker_password, |
| | 12 | citizen_id, citizen_full_name, citizen_email, citizen_phone, citizen_password, |
| | 13 | category_id, category_name, category_admin_id, |
| | 14 | report_id, report_description, location_text, latitude, longitude, |
| | 15 | report_status, report_priority, report_created_at, |
| | 16 | photo_id, image_url, |
| | 17 | log_id, log_status, log_changed_at, log_note, log_worker_id, |
| | 18 | comment_id, comment_content, comment_created_at, comment_worker_id, |
| | 19 | assignment_id, assignment_assigned_at, assignment_note, |
| | 20 | assignment_admin_id, assignment_worker_id) |
| | 21 | }}} |
| | 22 | |
| | 23 | == Функционални зависности == |
| | 24 | |
| | 25 | Канонично покривање на множеството функционални зависности кои важат секогаш и секаде во R (десните страни се групирани заради прегледност, според правилото за унија): |
| | 26 | |
| | 27 | {{{ |
| | 28 | ЗФ1: admin_id → admin_full_name, admin_email, admin_password |
| | 29 | ЗФ2: admin_email → admin_id |
| | 30 | ЗФ3: worker_id → worker_full_name, worker_email, worker_password |
| | 31 | ЗФ4: worker_email → worker_id |
| | 32 | ЗФ5: citizen_id → citizen_full_name, citizen_email, citizen_phone, citizen_password |
| | 33 | ЗФ6: citizen_email → citizen_id |
| | 34 | ЗФ7: category_id → category_name, category_admin_id |
| | 35 | ЗФ8: category_name → category_id |
| | 36 | ЗФ9: report_id → report_description, location_text, latitude, longitude, |
| | 37 | report_status, report_priority, report_created_at, |
| | 38 | citizen_id, category_id |
| | 39 | ЗФ10: photo_id → image_url, report_id |
| | 40 | ЗФ11: log_id → log_status, log_changed_at, log_note, log_worker_id, report_id |
| | 41 | ЗФ12: comment_id → comment_content, comment_created_at, comment_worker_id, report_id |
| | 42 | ЗФ13: assignment_id → assignment_assigned_at, assignment_note, |
| | 43 | assignment_admin_id, assignment_worker_id, report_id |
| | 44 | ЗФ14: report_id, assignment_worker_id → assignment_id |
| | 45 | }}} |
| | 46 | |
| | 47 | Објаснувања: |
| | 48 | * ЗФ2, ЗФ4, ЗФ6 и ЗФ8 важат бидејќи е-поштата и името на категоријата се единствени. |
| | 49 | * ЗФ14 важи бидејќи ист работник не може двапати да биде доделен на иста пријава. |
| | 50 | * Меѓу координатите и адресата нема функционална зависност, бидејќи адресата се внесува рачно и не е секогаш присутна. |
| | 51 | * Атрибутите со улоги (log_worker_id, comment_worker_id, assignment_worker_id, category_admin_id, assignment_admin_id) се поврзани со worker_id и admin_id преку зависности на вклучување (надворешни клучеви), а не преку функционални зависности. |
| | 52 | |
| | 53 | Покривањето е канонично: секоја лева страна е минимална, и ниту една зависност не може да се изведе од останатите. |
| | 54 | |
| | 55 | == Кандидат клучеви и примарен клуч == |
| | 56 | |
| | 57 | Атрибутите photo_id, log_id и comment_id не се појавуваат на десна страна во ниту една зависност, па мора да бидат во секој кандидат клуч. Исто така: |
| | 58 | * admin_id и admin_email се определуваат само еден со друг (ЗФ1, ЗФ2), па секој клуч содржи еден од нив; |
| | 59 | * worker_id и worker_email - исто (ЗФ3, ЗФ4); |
| | 60 | * assignment_id се определува со report_id и assignment_worker_id (ЗФ14), а report_id веќе се добива од photo_id (ЗФ10), па секој клуч содржи assignment_id или assignment_worker_id. |
| | 61 | |
| | 62 | Затворање на K = {photo_id, log_id, comment_id, assignment_id, admin_id, worker_id}: |
| | 63 | * од photo_id (ЗФ10) → report_id, па од ЗФ9 → сите атрибути на пријавата, citizen_id и category_id, па од ЗФ5 и ЗФ7 → сите атрибути на граѓанинот и категоријата; |
| | 64 | * од log_id, comment_id и assignment_id (ЗФ11-ЗФ13) → сите атрибути на статусите, коментарите и доделувањата; |
| | 65 | * од admin_id и worker_id (ЗФ1, ЗФ3) → атрибутите на администраторот и работникот. |
| | 66 | |
| | 67 | K+ ги содржи сите атрибути на R, а ниту еден вистински подмножество не ги содржи, па K е кандидат клуч. |
| | 68 | |
| | 69 | Сите кандидат клучеви имаат облик {photo_id, log_id, comment_id, A, W, X}, каде A ∈ {admin_id, admin_email}, W ∈ {worker_id, worker_email}, X ∈ {assignment_id, assignment_worker_id}, што дава 8 кандидат клучеви. |
| | 70 | |
| | 71 | '''Примарен клуч:''' {photo_id, log_id, comment_id, assignment_id, admin_id, worker_id}, бидејќи е составен само од вештачки нумерички идентификатори, кои се стабилни и кратки. |
| | 72 | |
| | 73 | '''Нормална форма на R:''' Сите атрибути се атомични (повеќе фотографии, статуси или коментари на една пријава се претставуваат со повеќе редови), па R е во 1НФ. R не е во 2НФ, бидејќи постојат непримарни атрибути кои зависат од дел од клучот (на пример admin_id → admin_full_name). |
| | 74 | |
| | 75 | == Декомпозиција во 1НФ == |
| | 76 | |
| | 77 | R е веќе во 1НФ, па не е потребна декомпозиција. |
| | 78 | |
| | 79 | == Декомпозиција во 2НФ == |
| | 80 | |
| | 81 | Во секој чекор, парцијалната зависност X → Y (X е дел од примарниот клуч) се отстранува со декомпозиција на релацијата во R1 = X ∪ Y и R2 = R − Y. Декомпозицијата е без загуба бидејќи R1 ∩ R2 = X, а X е клуч во R1. |
| | 82 | |
| | 83 | === Чекор 2.1 - Администратори === |
| | 84 | * '''Релација:''' R, клуч K. |
| | 85 | * '''Проблем:''' ЗФ1: admin_id → admin_full_name, admin_email, admin_password, каде admin_id е дел од клучот. |
| | 86 | * '''Резултат:''' R1(admin_id, admin_full_name, admin_email, admin_password) и Ra = R − {admin_full_name, admin_email, admin_password}. |
| | 87 | * '''Зависности и клучеви:''' во R1 важат ЗФ1 и ЗФ2, кандидат клучеви се admin_id и admin_email, примарен клуч admin_id. Во Ra важат сите останати зависности, а клучот е K. |
| | 88 | * '''Зачувување на зависностите:''' ЗФ1 и ЗФ2 се во R1, останатите се во Ra - зачувани. |
| | 89 | * '''Спојување без загуба:''' R1 ∩ Ra = {admin_id}, клуч во R1 - без загуба. |
| | 90 | |
| | 91 | === Чекор 2.2 - Работници === |
| | 92 | * '''Релација:''' Ra, клуч K. |
| | 93 | * '''Проблем:''' ЗФ3: worker_id → worker_full_name, worker_email, worker_password. |
| | 94 | * '''Резултат:''' R2(worker_id, worker_full_name, worker_email, worker_password) и Rb = Ra − {worker_full_name, worker_email, worker_password}. |
| | 95 | * '''Зависности и клучеви:''' во R2 важат ЗФ3 и ЗФ4, кандидат клучеви worker_id и worker_email, примарен клуч worker_id. Rb го задржува клучот K. |
| | 96 | * '''Зачувување на зависностите:''' зачувани. |
| | 97 | * '''Спојување без загуба:''' R2 ∩ Rb = {worker_id}, клуч во R2 - без загуба. |
| | 98 | |
| | 99 | === Чекор 2.3 - Фотографии === |
| | 100 | * '''Релација:''' Rb, клуч K. |
| | 101 | * '''Проблем:''' ЗФ10: photo_id → image_url, report_id. Атрибутот report_id се определува и со log_id, comment_id и assignment_id, па останува и во Rb, а се отстранува само image_url. |
| | 102 | * '''Резултат:''' R3(photo_id, image_url, report_id) и Rc = Rb − {image_url}. |
| | 103 | * '''Зависности и клучеви:''' во R3 важи ЗФ10, клуч photo_id. Rc го задржува клучот K. |
| | 104 | * '''Зачувување на зависностите:''' ЗФ10 е во R3 - зачувана. |
| | 105 | * '''Спојување без загуба:''' R3 ∩ Rc = {photo_id, report_id}, што содржи клуч на R3 - без загуба. |
| | 106 | |
| | 107 | === Чекор 2.4 - Историја на статуси === |
| | 108 | * '''Релација:''' Rc, клуч K. |
| | 109 | * '''Проблем:''' ЗФ11: log_id → log_status, log_changed_at, log_note, log_worker_id, report_id. |
| | 110 | * '''Резултат:''' R4(log_id, log_status, log_changed_at, log_note, log_worker_id, report_id) и Rd = Rc − {log_status, log_changed_at, log_note, log_worker_id}. |
| | 111 | * '''Зависности и клучеви:''' во R4 важи ЗФ11, клуч log_id. |
| | 112 | * '''Зачувување на зависностите:''' зачувани. |
| | 113 | * '''Спојување без загуба:''' R4 ∩ Rd = {log_id, report_id}, содржи клуч на R4 - без загуба. |
| | 114 | |
| | 115 | === Чекор 2.5 - Коментари === |
| | 116 | * '''Релација:''' Rd, клуч K. |
| | 117 | * '''Проблем:''' ЗФ12: comment_id → comment_content, comment_created_at, comment_worker_id, report_id. |
| | 118 | * '''Резултат:''' R5(comment_id, comment_content, comment_created_at, comment_worker_id, report_id) и Re = Rd − {comment_content, comment_created_at, comment_worker_id}. |
| | 119 | * '''Зависности и клучеви:''' во R5 важи ЗФ12, клуч comment_id. |
| | 120 | * '''Зачувување на зависностите:''' зачувани. |
| | 121 | * '''Спојување без загуба:''' R5 ∩ Re = {comment_id, report_id}, содржи клуч на R5 - без загуба. |
| | 122 | |
| | 123 | === Чекор 2.6 - Доделувања === |
| | 124 | * '''Релација:''' Re, клуч K. |
| | 125 | * '''Проблем:''' ЗФ13: assignment_id → assignment_assigned_at, assignment_note, assignment_admin_id, assignment_worker_id, report_id. |
| | 126 | * '''Резултат:''' R6(assignment_id, assignment_assigned_at, assignment_note, assignment_admin_id, assignment_worker_id, report_id) и Rf = Re − {assignment_assigned_at, assignment_note, assignment_admin_id, assignment_worker_id}. |
| | 127 | * '''Зависности и клучеви:''' во R6 важат ЗФ13 и ЗФ14. Кандидат клучеви се assignment_id и {report_id, assignment_worker_id}, примарен клуч assignment_id. Во Rf клучот е K (assignment_worker_id повеќе не е во Rf, па assignment_id е единствената можност за тој дел од клучот). |
| | 128 | * '''Зачувување на зависностите:''' ЗФ13 и ЗФ14 се во R6 - зачувани. |
| | 129 | * '''Спојување без загуба:''' R6 ∩ Rf = {assignment_id, report_id}, содржи клуч на R6 - без загуба. |
| | 130 | |
| | 131 | === Чекор 2.7 - Пријави === |
| | 132 | * '''Релација:''' Rf(photo_id, log_id, comment_id, assignment_id, admin_id, worker_id, report_id, атрибутите на пријавата, граѓанинот и категоријата), клуч K. |
| | 133 | * '''Проблем:''' photo_id → report_id и, преку ЗФ9, ЗФ5 и ЗФ7, photo_id → сите атрибути на пријавата, граѓанинот и категоријата. Тоа е парцијална зависност од клучот. |
| | 134 | * '''Резултат:''' R7(photo_id, report_id, report_description, location_text, latitude, longitude, report_status, report_priority, report_created_at, citizen_id, citizen_full_name, citizen_email, citizen_phone, citizen_password, category_id, category_name, category_admin_id) и Rg(photo_id, log_id, comment_id, assignment_id, admin_id, worker_id). |
| | 135 | * '''Зависности и клучеви:''' во R7 важат ЗФ5-ЗФ9 и photo_id → report_id, клуч photo_id. Rg нема нетривијални зависности, клучот се сите атрибути. |
| | 136 | * '''Зачувување на зависностите:''' зависностите log_id → report_id, comment_id → report_id и assignment_id → report_id не се во Rg, но веќе се зачувани во R4, R5 и R6. Останатите се во R7 - зачувани. |
| | 137 | * '''Спојување без загуба:''' R7 ∩ Rg = {photo_id}, клуч во R7 - без загуба. |
| | 138 | |
| | 139 | === Состојба по 2НФ === |
| | 140 | Сите релации се во 2НФ: R1-R5 и R7 имаат прост примарен клуч; во R6 непримарните атрибути зависат целосно од секој кандидат клуч; Rg нема непримарни атрибути. |
| | 141 | |
| | 142 | == Декомпозиција во 3НФ == |
| | 143 | |
| | 144 | Релациите R1-R6 и Rg се веќе во 3НФ: во нив секоја нетривијална зависност X → A има X кој е суперклуч (admin_email, worker_email, {report_id, assignment_worker_id} се кандидат клучеви). Единствено R7 содржи транзитивни зависности. |
| | 145 | |
| | 146 | === Чекор 3.1 - Издвојување на пријавата === |
| | 147 | * '''Релација:''' R7, клуч photo_id, релацијата е во 2НФ. |
| | 148 | * '''Проблем:''' транзитивна зависност photo_id → report_id → report_description, ..., citizen_id, category_id (ЗФ9), каде report_id не е суперклуч во R7. |
| | 149 | * '''Резултат:''' R8(report_id, report_description, location_text, latitude, longitude, report_status, report_priority, report_created_at, citizen_id, citizen_full_name, citizen_email, citizen_phone, citizen_password, category_id, category_name, category_admin_id) и R7a(photo_id, report_id). |
| | 150 | * '''Зависности и клучеви:''' во R8 важат ЗФ5-ЗФ9, клуч report_id. Во R7a важи photo_id → report_id, клуч photo_id. |
| | 151 | * '''Зачувување на зависностите:''' зачувани. |
| | 152 | * '''Спојување без загуба:''' R8 ∩ R7a = {report_id}, клуч во R8 - без загуба. |
| | 153 | * R7a е проекција на R3 со ист клуч (photo_id), па е вишок и се спојува со R3. |
| | 154 | |
| | 155 | === Чекор 3.2 - Издвојување на граѓанинот === |
| | 156 | * '''Релација:''' R8, клуч report_id. |
| | 157 | * '''Проблем:''' транзитивна зависност report_id → citizen_id → citizen_full_name, citizen_email, citizen_phone, citizen_password (ЗФ5). |
| | 158 | * '''Резултат:''' R9(citizen_id, citizen_full_name, citizen_email, citizen_phone, citizen_password) и R8a = R8 − {citizen_full_name, citizen_email, citizen_phone, citizen_password}. |
| | 159 | * '''Зависности и клучеви:''' во R9 важат ЗФ5 и ЗФ6, кандидат клучеви citizen_id и citizen_email, примарен клуч citizen_id. Во R8a важат ЗФ7, ЗФ8 и ЗФ9, клуч report_id. |
| | 160 | * '''Зачувување на зависностите:''' зачувани. |
| | 161 | * '''Спојување без загуба:''' R9 ∩ R8a = {citizen_id}, клуч во R9 - без загуба. |
| | 162 | |
| | 163 | === Чекор 3.3 - Издвојување на категоријата === |
| | 164 | * '''Релација:''' R8a, клуч report_id. |
| | 165 | * '''Проблем:''' транзитивна зависност report_id → category_id → category_name, category_admin_id (ЗФ7). |
| | 166 | * '''Резултат:''' R10(category_id, category_name, category_admin_id) и R11(report_id, report_description, location_text, latitude, longitude, report_status, report_priority, report_created_at, citizen_id, category_id). |
| | 167 | * '''Зависности и клучеви:''' во R10 важат ЗФ7 и ЗФ8, кандидат клучеви category_id и category_name, примарен клуч category_id. Во R11 важи ЗФ9, клуч report_id. |
| | 168 | * '''Зачувување на зависностите:''' зачувани. |
| | 169 | * '''Спојување без загуба:''' R10 ∩ R11 = {category_id}, клуч во R10 - без загуба. |
| | 170 | |
| | 171 | == BCNF == |
| | 172 | |
| | 173 | Сите добиени релации се во BCNF, бидејќи во секоја од нив левата страна на секоја нетривијална функционална зависност е суперклуч: |
| | 174 | * R1: admin_id и admin_email се кандидат клучеви. |
| | 175 | * R2: worker_id и worker_email се кандидат клучеви. |
| | 176 | * R9: citizen_id и citizen_email се кандидат клучеви. |
| | 177 | * R10: category_id и category_name се кандидат клучеви. |
| | 178 | * R3, R4, R5, R11: единствената лева страна е примарниот клуч. |
| | 179 | * R6: assignment_id и {report_id, assignment_worker_id} се кандидат клучеви. |
| | 180 | * Rg: нема нетривијални функционални зависности. |
| | 181 | |
| | 182 | Сите функционални зависности од почетното канонично покривање се зачувани (секоја е во барем една релација), а секој чекор е без загуба при спојување. |
| | 183 | |
| | 184 | == Конечен резултат и дискусија == |
| | 185 | |
| | 186 | === Нормализиран релационен модел === |
| | 187 | |
| | 188 | Релациите се преименувани според значењето, а атрибутите со улоги се преименувани во имиња на надворешни клучеви: |
| | 189 | |
| | 190 | {{{ |
| | 191 | admins (admin_id, full_name, email, password) ← R1 |
| | 192 | workers (worker_id, full_name, email, password) ← R2 |
| | 193 | citizens (citizen_id, full_name, email, phone, password) ← R9 |
| | 194 | categories (category_id, name, admin_id*) ← R10 |
| | 195 | reports (report_id, description, location_text, latitude, longitude, |
| | 196 | status, priority, created_at, citizen_id*, category_id*) ← R11 |
| | 197 | photos (photo_id, image_url, report_id*) ← R3 (+R7a) |
| | 198 | status_logs (log_id, status, changed_at, note, report_id*, worker_id*) ← R4 |
| | 199 | comments (comment_id, content, created_at, report_id*, worker_id*) ← R5 |
| | 200 | assignments (assignment_id, assigned_at, note, |
| | 201 | admin_id*, report_id*, worker_id*) ← R6 |
| | 202 | }}} |
| | 203 | |
| | 204 | Примарните клучеви се првиот атрибут во секоја релација, а со * се означени надворешните клучеви. Алтернативни клучеви: email во admins, workers и citizens; name во categories; {report_id, worker_id} во assignments. |
| | 205 | |
| | 206 | === Дискусија === |
| | 207 | |
| | 208 | Нормализацијата резултира со истите девет релации како и релациониот дизајн од фазата P2, со истите примарни клучеви, надворешни клучеви и единствени ограничувања. Со ова се потврдува дека дизајнот од P2 е во BCNF и дека не содржи редундантност предизвикана од функционални зависности. Затоа објектите во базата и документацијата од фазата P2 не се менуваат, и дизајнот од P2 се користи во следните фази. |
| | 209 | |
| | 210 | Преостанатата релација Rg(photo_id, log_id, comment_id, assignment_id, admin_id, worker_id) се состои само од клучот на почетната релација. Таа е потребна формално за спојувањето без загуба да ја врати целата униврзална релација, но не претставува ниту еден реален факт: секој нејзин ред е само комбинација на фотографија, статус, коментар и доделување на иста пријава, и произволен администратор и работник. Таа комбинација се добива со спојување на другите релации (повеќевредносни зависности, 4НФ), па Rg не се имплементира како табела. |
| | 211 | |
| | 212 | Атрибутот status во reports не е функционално зависен од другите атрибути на пријавата, но неговата вредност секогаш е еднаква на последниот статус во status_logs за таа пријава. Оваа редундантност не произлегува од функционална зависност, па нормализацијата не ја отстранува. Таа е намерно задржана, бидејќи овозможува брзо филтрирање на пријавите по статус без пребарување на целата историја. |
| | 213 | |
| | 214 | Атрибутите со улоги (log_worker_id, comment_worker_id, assignment_worker_id, category_admin_id, assignment_admin_id) во конечниот модел стануваат надворешни клучеви worker_id и admin_id кон табелите workers и admins, што одговара на зависностите на вклучување од почетниот модел. |