| | 1 | = Формална анализа и доказ за нормализација на Shifter = |
| | 2 | |
| | 3 | == 1. Дефиниција на универзалната релација == |
| | 4 | |
| | 5 | Тргнуваме од една единствена универзална релација која ги содржи сите атрибути од доменот. |
| | 6 | |
| | 7 | {{{ |
| | 8 | Universal_Relation_Shifter( |
| | 9 | user_id, user_name, user_email, user_login_provider, user_password_hash, user_verified, user_profile_complete, user_used_free_consultation, user_company_size, user_work_position, user_points, |
| | 10 | meeting_reminder_id, meeting_at, meeting_scheduled_at, meeting_sent, meeting_link, |
| | 11 | expert_id, expert_name, expert_email, expert_login_provider, expert_password_hash, |
| | 12 | course_id, course_image_url, course_color, course_difficulty, course_duration_minutes, course_price, |
| | 13 | course_version_id, version_number, version_creation_date, version_active, |
| | 14 | enrollment_id, enrollment_status, enrollment_purchase_date, enrollment_activation_date, enrollment_completion_date, |
| | 15 | payment_id, payment_amount, payment_date, payment_method, payment_status, |
| | 16 | review_id, review_rating, review_comment, review_date, |
| | 17 | course_translate_id, course_translate_language, course_title_short, course_title, course_description_short, course_description, course_description_long, |
| | 18 | course_what_will_be_learned_text, |
| | 19 | course_content_id, content_position, |
| | 20 | course_content_translate_id, content_translate_title, content_translate_language, |
| | 21 | course_lecture_id, lecture_duration_minutes, lecture_position, lecture_content_type, |
| | 22 | course_lecture_translate_id, lecture_title, lecture_language, lecture_content_file_name, lecture_description, lecture_content_text, |
| | 23 | user_course_progress_id, progress_completed, progress_completed_at, |
| | 24 | verification_token_uuid, token_created_at, token_expired_at, |
| | 25 | tag_id, tag_type, |
| | 26 | tag_translate_id, tag_translate_language, tag_value |
| | 27 | ) |
| | 28 | }}} |
| | 29 | |
| | 30 | ---- |
| | 31 | |
| | 32 | == 2. Функционални зависности == |
| | 33 | |
| | 34 | Ги идентификуваме сите функционални зависности што важат во универзалната релација: |
| | 35 | |
| | 36 | '''F = {''' |
| | 37 | * '''FD1:''' user_id → user_name, user_email, user_login_provider, user_password_hash, user_verified, user_profile_complete, user_used_free_consultation, user_company_size, user_work_position, user_points |
| | 38 | * '''FD2:''' verification_token_uuid → token_created_at, token_expired_at, user_id |
| | 39 | * '''FD3:''' meeting_reminder_id → meeting_at, meeting_scheduled_at, meeting_sent, meeting_link, user_id |
| | 40 | * '''FD4:''' expert_id → expert_name, expert_email, expert_login_provider, expert_password_hash |
| | 41 | * '''FD5:''' course_id → course_image_url, course_color, course_difficulty, course_duration_minutes, course_price |
| | 42 | * '''FD6:''' enrollment_id → enrollment_status, enrollment_purchase_date, enrollment_activation_date, enrollment_completion_date, user_id, course_version_id, payment_id, review_id |
| | 43 | * '''FD7:''' payment_id → payment_amount, payment_date, payment_method, payment_status, enrollment_id |
| | 44 | * '''FD8:''' review_id → review_rating, review_comment, review_date, enrollment_id |
| | 45 | * '''FD9:''' tag_id → tag_type |
| | 46 | * '''FD10:''' tag_translate_id → tag_translate_language, tag_value, tag_id |
| | 47 | * '''FD11:''' course_version_id → version_number, version_creation_date, version_active, course_id |
| | 48 | * '''FD12:''' course_translate_id → course_translate_language, course_title_short, course_title, course_description_short, course_description, course_description_long, course_id |
| | 49 | * '''FD13:''' course_content_id → content_position, course_version_id |
| | 50 | * '''FD14:''' course_content_translate_id → content_translate_title, content_translate_language, course_content_id |
| | 51 | * '''FD15:''' course_lecture_id → lecture_duration_minutes, lecture_position, lecture_content_type, course_content_id |
| | 52 | * '''FD16:''' course_lecture_translate_id → lecture_title, lecture_language, lecture_content_file_name, lecture_description, lecture_content_text, course_lecture_id |
| | 53 | * '''FD17:''' user_course_progress_id → progress_completed, progress_completed_at, enrollment_id, course_lecture_id |
| | 54 | * '''FD18:''' user_email → user_id |
| | 55 | * '''FD19:''' expert_email → expert_id |
| | 56 | * '''FD20:''' (course_id, version_number) → course_version_id |
| | 57 | * '''FD21:''' (course_id, course_translate_language) → course_translate_id |
| | 58 | * '''FD22:''' (course_lecture_id, lecture_language) → course_lecture_translate_id |
| | 59 | * '''FD23:''' (course_content_id, content_translate_language) → course_content_translate_id |
| | 60 | * '''FD24:''' (tag_id, tag_translate_language) → tag_translate_id |
| | 61 | * '''FD25:''' (enrollment_id, course_lecture_id) → user_course_progress_id |
| | 62 | * '''FD26:''' (course_version_id, content_position) → course_content_id |
| | 63 | * '''FD27:''' (course_content_id, lecture_position) → course_lecture_id |
| | 64 | |
| | 65 | '''}''' |
| | 66 | |
| | 67 | ---- |
| | 68 | |
| | 69 | == 3. Кандидат клучеви и примарен клуч == |
| | 70 | |
| | 71 | === 3.1 Определување на кандидат клучеви === |
| | 72 | |
| | 73 | За да најдеме кандидат клуч, треба да најдеме минимално множество атрибути чиј затворач ги содржи сите атрибути на релацијата. |
| | 74 | |
| | 75 | === 3.2 Класификација на атрибути (Лева / Десна страна) === |
| | 76 | |
| | 77 | ||= Атрибут =||= Лева страна =||= Десна страна =||= Класификација =|| |
| | 78 | || userId || ✓ (FD1) || ✓ (FD2, FD3, FD10) || и лево и десно || |
| | 79 | || user_username || ✓ (FD2) || ✓ (FD1, FD3) || и лево и десно || |
| | 80 | || user_email || ✓ (FD3) || ✓ (FD1, FD2) || и лево и десно || |
| | 81 | || user_password || ✗ || ✓ (FD1, FD2, FD3) || Само десно || |
| | 82 | || user_full_name || ✗ || ✓ (FD1, FD2, FD3) || Само десно || |
| | 83 | || user_role || ✗ || ✓ (FD1, FD2, FD3) || Само десно || |
| | 84 | || user_is_active || ✗ || ✓ (FD1, FD2, FD3) || Само десно || |
| | 85 | || customerId || ✓ (FD4) || ✓ (FD10) || и лево и десно || |
| | 86 | || customer_name || ✗ || ✓ (FD4) || Само десно || |
| | 87 | || customer_email || ✗ || ✓ (FD4) || Само десно || |
| | 88 | || customer_phone || ✗ || ✓ (FD4) || Само десно || |
| | 89 | || customer_address || ✗ || ✓ (FD4) || Само десно || |
| | 90 | || productId || ✓ (FD5) || ✓ (FD6) || и лево и десно || |
| | 91 | || product_sku || ✓ (FD6) || ✓ (FD5) || и лево и десно || |
| | 92 | || product_name || ✗ || ✓ (FD5, FD6) || Само десно || |
| | 93 | || product_description || ✗ || ✓ (FD5, FD6) || Само десно || |
| | 94 | || product_unit_price || ✗ || ✓ (FD5, FD6) || Само десно || |
| | 95 | || product_reorder_level || ✗ || ✓ (FD5, FD6) || Само десно || |
| | 96 | || categoryId || ✓ (FD7) || ✓ (FD5, FD6) || и лево и десно || |
| | 97 | || category_name || ✗ || ✓ (FD7) || Само десно || |
| | 98 | || category_description || ✗ || ✓ (FD7) || Само десно || |
| | 99 | || supplierId || ✓ (FD8) || ✓ (FD5, FD6, FD11) || и лево и десно || |
| | 100 | || supplier_name || ✗ || ✓ (FD8) || Само десно || |
| | 101 | || supplier_contact_person || ✗ || ✓ (FD8) || Само десно || |
| | 102 | || supplier_phone || ✗ || ✓ (FD8) || Само десно || |
| | 103 | || supplier_email || ✗ || ✓ (FD8) || Само десно || |
| | 104 | || supplier_address || ✗ || ✓ (FD8) || Само десно || |
| | 105 | || warehouseId || ✓ (FD9) || ✓ (FD10, FD11, FD14) || и лево и десно || |
| | 106 | || warehouse_name || ✗ || ✓ (FD9) || Само десно || |
| | 107 | || warehouse_location || ✗ || ✓ (FD9) || Само десно || |
| | 108 | || warehouse_capacity || ✗ || ✓ (FD9) || Само десно || |
| | 109 | || '''saleId''' || ✓ (FD10, FD12) || ✗ || '''Само лево''' || |
| | 110 | || sale_date_time || ✗ || ✓ (FD10) || Само десно || |
| | 111 | || sale_total_amount || ✗ || ✓ (FD10) || Само десно || |
| | 112 | || '''poId''' || ✓ (FD11, FD13) || ✗ || '''Само лево''' || |
| | 113 | || po_order_date || ✗ || ✓ (FD11) || Само десно || |
| | 114 | || po_expected_delivery_date || ✗ || ✓ (FD11) || Само десно || |
| | 115 | || po_status || ✗ || ✓ (FD11) || Само десно || |
| | 116 | || saleitem_quantity || ✗ || ✓ (FD12) || Само десно || |
| | 117 | || saleitem_unit_price_at_sale || ✗ || ✓ (FD12) || Само десно || |
| | 118 | || poitem_quantity || ✗ || ✓ (FD13) || Само десно || |
| | 119 | || poitem_unit_cost || ✗ || ✓ (FD13) || Само десно || |
| | 120 | || stock_quantity_on_hand || ✗ || ✓ (FD14) || Само десно || |
| | 121 | || stock_last_updated || ✗ || ✓ (FD14) || Само десно || |
| | 122 | |
| | 123 | |
| | 124 | |
| | 125 | |
| | 126 | === 3.3 Атрибути кои се појавуваат САМО на лева страна === |
| | 127 | |
| | 128 | * '''saleId''' - мора да биде дел од секој кандидат клуч |
| | 129 | * '''poId''' - мора да биде дел од секој кандидат клуч |
| | 130 | |
| | 131 | === 3.4 Пресметка на затворач === |
| | 132 | |
| | 133 | '''Чекор 1:''' Започнуваме со {saleId, poId} и пресметуваме затворач: |
| | 134 | |
| | 135 | {{{ |
| | 136 | {saleId, poId}⁺: |
| | 137 | - Од FD10 (saleId →): добиваме sale_date_time, sale_total_amount, userId, customerId, warehouseId |
| | 138 | - Од FD11 (poId →): добиваме po_order_date, po_expected_delivery_date, po_status, supplierId, warehouseId |
| | 139 | - Од FD1 (userId →): добиваме user_username, user_password, user_full_name, user_email, user_role, user_is_active |
| | 140 | - Од FD4 (customerId →): добиваме customer_name, customer_email, customer_phone, customer_address |
| | 141 | - Од FD8 (supplierId →): добиваме supplier_name, supplier_contact_person, supplier_phone, supplier_email, supplier_address |
| | 142 | - Од FD9 (warehouseId →): добиваме warehouse_name, warehouse_location, warehouse_capacity |
| | 143 | }}} |
| | 144 | |
| | 145 | '''{saleId, poId}⁺ ≠ U''' |
| | 146 | |
| | 147 | '''Недостасуваат:''' productId, product_name, product_description, product_sku, product_unit_price, product_reorder_level, categoryId, category_name, category_description, saleitem_quantity, saleitem_unit_price_at_sale, poitem_quantity, poitem_unit_cost, stock_quantity_on_hand, stock_last_updated |
| | 148 | |
| | 149 | '''Чекор 2:''' Додаваме productId: |
| | 150 | |
| | 151 | {{{ |
| | 152 | {saleId, poId, productId}⁺: |
| | 153 | - Сè од претходно, плус: |
| | 154 | - Од FD5 (productId →): добиваме product_name, product_description, product_sku, product_unit_price, product_reorder_level, categoryId, supplierId |
| | 155 | - Од FD7 (categoryId →): добиваме category_name, category_description |
| | 156 | - Од FD12 ({saleId, productId} →): добиваме saleitem_quantity, saleitem_unit_price_at_sale |
| | 157 | - Од FD13 ({poId, productId} →): добиваме poitem_quantity, poitem_unit_cost |
| | 158 | - Од FD14 ({warehouseId, productId} →): добиваме stock_quantity_on_hand, stock_last_updated |
| | 159 | }}} |
| | 160 | |
| | 161 | '''{saleId, poId, productId}⁺ = U''' (ги содржи сите атрибути) ✓ |
| | 162 | |
| | 163 | === 3.5 Проверка за минималност === |
| | 164 | |
| | 165 | ||= Подмножество =||= Затворач =||= = U? =|| |
| | 166 | || {saleId, poId}⁺ || Недостасуваат атрибути поврзани со productId || ✗ НЕ || |
| | 167 | || {saleId, productId}⁺ || Недостасуваат атрибути од poId: po_order_date, po_expected_delivery_date, po_status, poitem_quantity, poitem_unit_cost || ✗ НЕ || |
| | 168 | || {poId, productId}⁺ || Недостасуваат атрибути од saleId: sale_date_time, sale_total_amount, saleitem_quantity, saleitem_unit_price_at_sale, userId→атрибути, customerId→атрибути || ✗ НЕ || |
| | 169 | |
| | 170 | '''Заклучок:''' {saleId, poId, productId} е минимален и е кандидат клуч. |
| | 171 | |
| | 172 | === 3.6 Други кандидат клучеви === |
| | 173 | |
| | 174 | Бидејќи постојат еквиваленции (FD5 и FD6 покажуваат дека productId ↔ product_sku), можеме да го замениме productId со product_sku: |
| | 175 | |
| | 176 | '''Кандидат клуч 2:''' {saleId, poId, product_sku} |
| | 177 | |
| | 178 | {{{ |
| | 179 | Проверка: {saleId, poId, product_sku}⁺: |
| | 180 | - Од FD6 (product_sku →): добиваме productId и сите атрибути на продукт |
| | 181 | - Понатаму идентично како горе |
| | 182 | |
| | 183 | {saleId, poId, product_sku}⁺ = U ✓ |
| | 184 | }}} |
| | 185 | |
| | 186 | === 3.7 Избор на примарен клуч === |
| | 187 | |
| | 188 | ||= Кандидат клуч =||= Број на атрибути =||= Тип на атрибути =|| |
| | 189 | || {saleId, poId, productId} || 3 || Сурогат клучеви (стабилни) || |
| | 190 | || {saleId, poId, product_sku} || 3 || Мешано (saleId, poId сурогат; sku природен) || |
| | 191 | |
| | 192 | '''Избран примарен клуч: {saleId, poId, productId}''' |
| | 193 | |
| | 194 | '''Образложение:''' |
| | 195 | 1. Сите три атрибути се сурогат клучеви кои не се менуваат со тек на време |
| | 196 | 2. productId е понумерички и поефикасен за индексирање од product_sku (кој е VARCHAR) |
| | 197 | 3. Сурогат клучевите обезбедуваат стабилност - ако се промени SKU кодот на продукт, примарниот клуч останува непроменет |
| | 198 | |
| | 199 | ---- |
| | 200 | |
| | 201 | == 4. 1NF Декомпозиција == |
| | 202 | |
| | 203 | === 4.1 Проверка за 1NF === |
| | 204 | |
| | 205 | Универзалната релација U е во 1NF бидејќи: |
| | 206 | * Сите атрибути се атомски (нема повеќевредносни атрибути) |
| | 207 | * Постои примарен клуч: {saleId, poId, productId} |
| | 208 | * Нема повторувачки групи |
| | 209 | |
| | 210 | '''Заклучок:''' U е во 1NF. ✓ |
| | 211 | |
| | 212 | === 4.2 Проверка која FD ја нарушува 2NF === |
| | 213 | |
| | 214 | За да биде во 2NF, секој не-клучен атрибут мора целосно да зависи од примарниот клуч (нема парцијални зависности). |
| | 215 | |
| | 216 | Примарен клуч: '''{saleId, poId, productId}''' |
| | 217 | |
| | 218 | '''Парцијални зависности (атрибути кои зависат од дел од клучот):''' |
| | 219 | |
| | 220 | ||= FD =||= Детерминант =||= Дел од клуч? =||= Парцијална зависност? =|| |
| | 221 | || FD1: userId → user_* || userId || НЕ (транзитивно преку saleId) || Не директно || |
| | 222 | || FD5: productId → product_*, categoryId, supplierId || productId || ДА (productId е дел од клучот) || '''ДА''' || |
| | 223 | || FD10: saleId → sale_*, userId, customerId, warehouseId || saleId || ДА (saleId е дел од клучот) || '''ДА''' || |
| | 224 | || FD11: poId → po_*, supplierId, warehouseId || poId || ДА (poId е дел од клучот) || '''ДА''' || |
| | 225 | || FD12: {saleId, productId} → saleitem_* || {saleId, productId} || ДА (подмножество на клучот) || '''ДА''' || |
| | 226 | || FD13: {poId, productId} → poitem_* || {poId, productId} || ДА (подмножество на клучот) || '''ДА''' || |
| | 227 | || FD14: {warehouseId, productId} → stock_* || {warehouseId, productId} || warehouseId не е во клучот директно || Посебен случај* || |
| | 228 | |
| | 229 | *Забелешка за FD14: warehouseId се добива транзитивно од saleId (FD10) и од poId (FD11), но {warehouseId, productId} не е подмножество на примарниот клуч. |
| | 230 | |
| | 231 | '''Идентификувани парцијални зависности што ја нарушуваат 2NF:''' |
| | 232 | |
| | 233 | 1. '''FD5:''' productId → product_name, product_description, product_sku, product_unit_price, product_reorder_level, categoryId, supplierId |
| | 234 | 2. '''FD10:''' saleId → sale_date_time, sale_total_amount, userId, customerId, warehouseId |
| | 235 | 3. '''FD11:''' poId → po_order_date, po_expected_delivery_date, po_status, supplierId, warehouseId |
| | 236 | 4. '''FD12:''' {saleId, productId} → saleitem_quantity, saleitem_unit_price_at_sale |
| | 237 | 5. '''FD13:''' {poId, productId} → poitem_quantity, poitem_unit_cost |
| | 238 | |
| | 239 | ---- |
| | 240 | |
| | 241 | == 5. 2NF Декомпозиција == |
| | 242 | |
| | 243 | === Чекор 5.1: Декомпозиција по FD5 (productId → ...) === |
| | 244 | |
| | 245 | '''Нарушува 2NF:''' productId е дел од примарниот клуч, а product_name, product_description, итн. зависат само од productId. |
| | 246 | |
| | 247 | '''Декомпозиција:''' |
| | 248 | |
| | 249 | {{{ |
| | 250 | R1(productId, product_name, product_description, product_sku, product_unit_price, product_reorder_level, categoryId, supplierId) |
| | 251 | PK: productId |
| | 252 | |
| | 253 | U1 = U - {product_name, product_description, product_sku, product_unit_price, product_reorder_level} |
| | 254 | |
| | 255 | U1(saleId, poId, productId, userId, user_username, user_password, user_full_name, user_email, user_role, user_is_active, customerId, customer_name, customer_email, customer_phone, customer_address, categoryId, category_name, category_description, supplierId, supplier_name, supplier_contact_person, supplier_phone, supplier_email, supplier_address, warehouseId, warehouse_name, warehouse_location, warehouse_capacity, sale_date_time, sale_total_amount, po_order_date, po_expected_delivery_date, po_status, saleitem_quantity, saleitem_unit_price_at_sale, poitem_quantity, poitem_unit_cost, stock_quantity_on_hand, stock_last_updated) |
| | 256 | }}} |
| | 257 | |
| | 258 | '''Проверка за lossless join:''' |
| | 259 | * R1 ∩ U1 = {productId, categoryId, supplierId} |
| | 260 | * productId → R1 (FD5) |
| | 261 | * Декомпозицијата е lossless ✓ |
| | 262 | |
| | 263 | '''Проверка за зачувување на FD:''' FD5 е зачувана во R1 ✓ |
| | 264 | |
| | 265 | === Чекор 5.2: Декомпозиција по FD10 (saleId → ...) === |
| | 266 | |
| | 267 | '''Нарушува 2NF:''' saleId е дел од примарниот клуч на U1. |
| | 268 | |
| | 269 | '''Декомпозиција:''' |
| | 270 | |
| | 271 | {{{ |
| | 272 | R2(saleId, sale_date_time, sale_total_amount, userId, customerId, warehouseId) |
| | 273 | PK: saleId |
| | 274 | |
| | 275 | U2 = U1 - {sale_date_time, sale_total_amount} |
| | 276 | |
| | 277 | U2(saleId, poId, productId, userId, user_username, user_password, user_full_name, user_email, user_role, user_is_active, customerId, customer_name, customer_email, customer_phone, customer_address, categoryId, category_name, category_description, supplierId, supplier_name, supplier_contact_person, supplier_phone, supplier_email, supplier_address, warehouseId, warehouse_name, warehouse_location, warehouse_capacity, po_order_date, po_expected_delivery_date, po_status, saleitem_quantity, saleitem_unit_price_at_sale, poitem_quantity, poitem_unit_cost, stock_quantity_on_hand, stock_last_updated) |
| | 278 | }}} |
| | 279 | |
| | 280 | '''Проверка за lossless join:''' |
| | 281 | * R2 ∩ U2 = {saleId, userId, customerId, warehouseId} |
| | 282 | * saleId → R2 (FD10) |
| | 283 | * Декомпозицијата е lossless ✓ |
| | 284 | |
| | 285 | === Чекор 5.3: Декомпозиција по FD11 (poId → ...) === |
| | 286 | |
| | 287 | '''Нарушува 2NF:''' poId е дел од примарниот клуч на U2. |
| | 288 | |
| | 289 | '''Декомпозиција:''' |
| | 290 | |
| | 291 | {{{ |
| | 292 | R3(poId, po_order_date, po_expected_delivery_date, po_status, supplierId, warehouseId) |
| | 293 | PK: poId |
| | 294 | |
| | 295 | U3 = U2 - {po_order_date, po_expected_delivery_date, po_status} |
| | 296 | |
| | 297 | U3(saleId, poId, productId, userId, user_username, user_password, user_full_name, user_email, user_role, user_is_active, customerId, customer_name, customer_email, customer_phone, customer_address, categoryId, category_name, category_description, supplierId, supplier_name, supplier_contact_person, supplier_phone, supplier_email, supplier_address, warehouseId, warehouse_name, warehouse_location, warehouse_capacity, saleitem_quantity, saleitem_unit_price_at_sale, poitem_quantity, poitem_unit_cost, stock_quantity_on_hand, stock_last_updated) |
| | 298 | }}} |
| | 299 | |
| | 300 | '''Проверка за lossless join:''' |
| | 301 | * R3 ∩ U3 = {poId, supplierId, warehouseId} |
| | 302 | * poId → R3 (FD11) |
| | 303 | * Декомпозицијата е lossless ✓ |
| | 304 | |
| | 305 | === Чекор 5.4: Декомпозиција по FD12 ({saleId, productId} → ...) === |
| | 306 | |
| | 307 | '''Нарушува 2NF:''' {saleId, productId} е подмножество на примарниот клуч. |
| | 308 | |
| | 309 | '''Декомпозиција:''' |
| | 310 | |
| | 311 | {{{ |
| | 312 | R4(saleId, productId, saleitem_quantity, saleitem_unit_price_at_sale) |
| | 313 | PK: {saleId, productId} |
| | 314 | |
| | 315 | U4 = U3 - {saleitem_quantity, saleitem_unit_price_at_sale} |
| | 316 | |
| | 317 | U4(saleId, poId, productId, userId, user_username, user_password, user_full_name, user_email, user_role, user_is_active, customerId, customer_name, customer_email, customer_phone, customer_address, categoryId, category_name, category_description, supplierId, supplier_name, supplier_contact_person, supplier_phone, supplier_email, supplier_address, warehouseId, warehouse_name, warehouse_location, warehouse_capacity, poitem_quantity, poitem_unit_cost, stock_quantity_on_hand, stock_last_updated) |
| | 318 | }}} |
| | 319 | |
| | 320 | '''Проверка за lossless join:''' |
| | 321 | * R4 ∩ U4 = {saleId, productId} |
| | 322 | * {saleId, productId} → R4 (FD12) |
| | 323 | * Декомпозицијата е lossless ✓ |
| | 324 | |
| | 325 | === Чекор 5.5: Декомпозиција по FD13 ({poId, productId} → ...) === |
| | 326 | |
| | 327 | '''Нарушува 2NF:''' {poId, productId} е подмножество на примарниот клуч. |
| | 328 | |
| | 329 | '''Декомпозиција:''' |
| | 330 | |
| | 331 | {{{ |
| | 332 | R5(poId, productId, poitem_quantity, poitem_unit_cost) |
| | 333 | PK: {poId, productId} |
| | 334 | |
| | 335 | U5 = U4 - {poitem_quantity, poitem_unit_cost} |
| | 336 | |
| | 337 | U5(saleId, poId, productId, userId, user_username, user_password, user_full_name, user_email, user_role, user_is_active, customerId, customer_name, customer_email, customer_phone, customer_address, categoryId, category_name, category_description, supplierId, supplier_name, supplier_contact_person, supplier_phone, supplier_email, supplier_address, warehouseId, warehouse_name, warehouse_location, warehouse_capacity, stock_quantity_on_hand, stock_last_updated) |
| | 338 | }}} |
| | 339 | |
| | 340 | '''Проверка за lossless join:''' |
| | 341 | * R5 ∩ U5 = {poId, productId} |
| | 342 | * {poId, productId} → R5 (FD13) |
| | 343 | * Декомпозицијата е lossless ✓ |
| | 344 | |
| | 345 | === Состојба после 2NF декомпозиција === |
| | 346 | |
| | 347 | {{{ |
| | 348 | R1(productId, product_name, product_description, product_sku, product_unit_price, product_reorder_level, categoryId, supplierId) |
| | 349 | PK: productId |
| | 350 | |
| | 351 | R2(saleId, sale_date_time, sale_total_amount, userId, customerId, warehouseId) |
| | 352 | PK: saleId |
| | 353 | |
| | 354 | R3(poId, po_order_date, po_expected_delivery_date, po_status, supplierId, warehouseId) |
| | 355 | PK: poId |
| | 356 | |
| | 357 | R4(saleId, productId, saleitem_quantity, saleitem_unit_price_at_sale) |
| | 358 | PK: {saleId, productId} |
| | 359 | |
| | 360 | R5(poId, productId, poitem_quantity, poitem_unit_cost) |
| | 361 | PK: {poId, productId} |
| | 362 | |
| | 363 | U5(saleId, poId, productId, userId, user_username, user_password, user_full_name, user_email, user_role, user_is_active, |
| | 364 | customerId, customer_name, customer_email, customer_phone, customer_address, |
| | 365 | categoryId, category_name, category_description, |
| | 366 | supplierId, supplier_name, supplier_contact_person, supplier_phone, supplier_email, supplier_address, |
| | 367 | warehouseId, warehouse_name, warehouse_location, warehouse_capacity, |
| | 368 | stock_quantity_on_hand, stock_last_updated) |
| | 369 | PK: {saleId, poId, productId} |
| | 370 | }}} |
| | 371 | |
| | 372 | ---- |
| | 373 | |
| | 374 | == 6. 3NF Декомпозиција == |
| | 375 | |
| | 376 | === Проверка за транзитивни зависности во секоја релација === |
| | 377 | |
| | 378 | ==== R1(productId, product_name, product_description, product_sku, product_unit_price, product_reorder_level, categoryId, supplierId) ==== |
| | 379 | |
| | 380 | '''Транзитивни зависности:''' |
| | 381 | * productId → categoryId → category_name, category_description? |
| | 382 | * category_name и category_description НЕ се во R1, па нема транзитивна зависност во оваа релација. |
| | 383 | * productId → supplierId → supplier_*? |
| | 384 | * supplier_* атрибутите НЕ се во R1. |
| | 385 | |
| | 386 | '''Заклучок:''' R1 е во 3NF ✓ |
| | 387 | |
| | 388 | ==== R2(saleId, sale_date_time, sale_total_amount, userId, customerId, warehouseId) ==== |
| | 389 | |
| | 390 | '''Транзитивни зависности:''' |
| | 391 | * saleId → userId → user_*? |
| | 392 | * user_* атрибутите НЕ се во R2. |
| | 393 | * saleId → customerId → customer_*? |
| | 394 | * customer_* атрибутите НЕ се во R2. |
| | 395 | * saleId → warehouseId → warehouse_*? |
| | 396 | * warehouse_* атрибутите НЕ се во R2. |
| | 397 | |
| | 398 | '''Заклучок:''' R2 е во 3NF ✓ |
| | 399 | |
| | 400 | ==== R3(poId, po_order_date, po_expected_delivery_date, po_status, supplierId, warehouseId) ==== |
| | 401 | |
| | 402 | '''Транзитивни зависности:''' |
| | 403 | * Слично како R2, supplier_* и warehouse_* не се во R3. |
| | 404 | |
| | 405 | '''Заклучок:''' R3 е во 3NF ✓ |
| | 406 | |
| | 407 | ==== R4(saleId, productId, saleitem_quantity, saleitem_unit_price_at_sale) ==== |
| | 408 | |
| | 409 | '''Транзитивни зависности:''' |
| | 410 | * Нема не-клучни атрибути кои одредуваат други не-клучни атрибути. |
| | 411 | |
| | 412 | '''Заклучок:''' R4 е во 3NF ✓ |
| | 413 | |
| | 414 | ==== R5(poId, productId, poitem_quantity, poitem_unit_cost) ==== |
| | 415 | |
| | 416 | '''Заклучок:''' R5 е во 3NF ✓ |
| | 417 | |
| | 418 | ==== U5 - Проверка за транзитивни зависности ==== |
| | 419 | |
| | 420 | Примарен клуч на U5: '''{saleId, poId, productId}''' |
| | 421 | |
| | 422 | '''Транзитивни зависности во U5:''' |
| | 423 | |
| | 424 | 1. '''FD1 транзитивно:''' {saleId, poId, productId} → userId (преку R2/saleId) → user_username, user_password, user_full_name, user_email, user_role, user_is_active |
| | 425 | * userId не е клуч на U5, а user_* зависат од userId |
| | 426 | * '''НАРУШУВА 3NF''' |
| | 427 | |
| | 428 | 2. '''FD4 транзитивно:''' {saleId, poId, productId} → customerId (преку R2/saleId) → customer_name, customer_email, customer_phone, customer_address |
| | 429 | * '''НАРУШУВА 3NF''' |
| | 430 | |
| | 431 | 3. '''FD7 транзитивно:''' {saleId, poId, productId} → categoryId (преку R1/productId) → category_name, category_description |
| | 432 | * '''НАРУШУВА 3NF''' |
| | 433 | |
| | 434 | 4. '''FD8 транзитивно:''' {saleId, poId, productId} → supplierId → supplier_name, supplier_contact_person, supplier_phone, supplier_email, supplier_address |
| | 435 | * '''НАРУШУВА 3NF''' |
| | 436 | |
| | 437 | 5. '''FD9 транзитивно:''' {saleId, poId, productId} → warehouseId → warehouse_name, warehouse_location, warehouse_capacity |
| | 438 | * '''НАРУШУВА 3NF''' |
| | 439 | |
| | 440 | 6. '''FD14:''' {warehouseId, productId} → stock_quantity_on_hand, stock_last_updated |
| | 441 | * warehouseId се добива транзитивно, а {warehouseId, productId} не е клуч на U5 |
| | 442 | * '''НАРУШУВА 3NF''' |
| | 443 | |
| | 444 | === Чекор 6.1: Декомпозиција по FD1 (userId → user_*) === |
| | 445 | |
| | 446 | {{{ |
| | 447 | R6(userId, user_username, user_password, user_full_name, user_email, user_role, user_is_active) |
| | 448 | PK: userId |
| | 449 | |
| | 450 | U6 = U5 - {user_username, user_password, user_full_name, user_email, user_role, user_is_active} |
| | 451 | |
| | 452 | U6(saleId, poId, productId, userId, customerId, customer_name, customer_email, |
| | 453 | customer_phone, customer_address, categoryId, category_name, category_description, |
| | 454 | supplierId, supplier_name, supplier_contact_person, supplier_phone, supplier_email, |
| | 455 | supplier_address, warehouseId, warehouse_name, warehouse_location, warehouse_capacity, |
| | 456 | stock_quantity_on_hand, stock_last_updated) |
| | 457 | }}} |
| | 458 | |
| | 459 | '''Проверка за lossless join:''' |
| | 460 | * R6 ∩ U6 = {userId} |
| | 461 | * userId → R6 (FD1) |
| | 462 | * Lossless ✓ |
| | 463 | |
| | 464 | === Чекор 6.2: Декомпозиција по FD4 (customerId → customer_*) === |
| | 465 | |
| | 466 | {{{ |
| | 467 | R7(customerId, customer_name, customer_email, customer_phone, customer_address) |
| | 468 | PK: customerId |
| | 469 | |
| | 470 | U7 = U6 - {customer_name, customer_email, customer_phone, customer_address} |
| | 471 | |
| | 472 | U7(saleId, poId, productId, userId, customerId, categoryId, category_name, |
| | 473 | category_description, supplierId, supplier_name, supplier_contact_person, |
| | 474 | supplier_phone, supplier_email, supplier_address, warehouseId, warehouse_name, |
| | 475 | warehouse_location, warehouse_capacity, stock_quantity_on_hand, stock_last_updated) |
| | 476 | }}} |
| | 477 | |
| | 478 | '''Проверка за lossless join:''' |
| | 479 | * R7 ∩ U7 = {customerId} |
| | 480 | * customerId → R7 (FD4) |
| | 481 | * Lossless ✓ |
| | 482 | |
| | 483 | === Чекор 6.3: Декомпозиција по FD7 (categoryId → category_*) === |
| | 484 | |
| | 485 | {{{ |
| | 486 | R8(categoryId, category_name, category_description) |
| | 487 | PK: categoryId |
| | 488 | |
| | 489 | U8 = U7 - {category_name, category_description} |
| | 490 | |
| | 491 | U8(saleId, poId, productId, userId, customerId, categoryId, supplierId, |
| | 492 | supplier_name, supplier_contact_person, supplier_phone, supplier_email, |
| | 493 | supplier_address, warehouseId, warehouse_name, warehouse_location, |
| | 494 | warehouse_capacity, stock_quantity_on_hand, stock_last_updated) |
| | 495 | }}} |
| | 496 | |
| | 497 | '''Проверка за lossless join:''' |
| | 498 | * R8 ∩ U8 = {categoryId} |
| | 499 | * categoryId → R8 (FD7) |
| | 500 | * Lossless ✓ |
| | 501 | |
| | 502 | === Чекор 6.4: Декомпозиција по FD8 (supplierId → supplier_*) === |
| | 503 | |
| | 504 | {{{ |
| | 505 | R9(supplierId, supplier_name, supplier_contact_person, supplier_phone, supplier_email, supplier_address) |
| | 506 | PK: supplierId |
| | 507 | |
| | 508 | U9 = U8 - {supplier_name, supplier_contact_person, supplier_phone, supplier_email, supplier_address} |
| | 509 | |
| | 510 | U9(saleId, poId, productId, userId, customerId, categoryId, supplierId, |
| | 511 | warehouseId, warehouse_name, warehouse_location, warehouse_capacity, |
| | 512 | stock_quantity_on_hand, stock_last_updated) |
| | 513 | }}} |
| | 514 | |
| | 515 | '''Проверка за lossless join:''' |
| | 516 | * R9 ∩ U9 = {supplierId} |
| | 517 | * supplierId → R9 (FD8) |
| | 518 | * Lossless ✓ |
| | 519 | |
| | 520 | === Чекор 6.5: Декомпозиција по FD9 (warehouseId → warehouse_*) === |
| | 521 | |
| | 522 | {{{ |
| | 523 | R10(warehouseId, warehouse_name, warehouse_location, warehouse_capacity) |
| | 524 | PK: warehouseId |
| | 525 | |
| | 526 | U10 = U9 - {warehouse_name, warehouse_location, warehouse_capacity} |
| | 527 | |
| | 528 | U10(saleId, poId, productId, userId, customerId, categoryId, supplierId, warehouseId, stock_quantity_on_hand, stock_last_updated) |
| | 529 | }}} |
| | 530 | |
| | 531 | '''Проверка за lossless join:''' |
| | 532 | * R10 ∩ U10 = {warehouseId} |
| | 533 | * warehouseId → R10 (FD9) |
| | 534 | * Lossless ✓ |
| | 535 | |
| | 536 | === Чекор 6.6: Декомпозиција по FD14 ({warehouseId, productId} → stock_*) === |
| | 537 | |
| | 538 | {{{ |
| | 539 | R11(warehouseId, productId, stock_quantity_on_hand, stock_last_updated) |
| | 540 | PK: {warehouseId, productId} |
| | 541 | |
| | 542 | U11 = U10 - {stock_quantity_on_hand, stock_last_updated} |
| | 543 | |
| | 544 | U11(saleId, poId, productId, userId, customerId, categoryId, supplierId, warehouseId) |
| | 545 | }}} |
| | 546 | |
| | 547 | '''Проверка за lossless join:''' |
| | 548 | * R11 ∩ U11 = {warehouseId, productId} |
| | 549 | * {warehouseId, productId} → R11 (FD14) |
| | 550 | * Lossless ✓ |
| | 551 | |
| | 552 | === Анализа на U11 === |
| | 553 | |
| | 554 | {{{ |
| | 555 | U11(saleId, poId, productId, userId, customerId, categoryId, supplierId, warehouseId) |
| | 556 | PK: {saleId, poId, productId} |
| | 557 | }}} |
| | 558 | |
| | 559 | Атрибутите userId, customerId, warehouseId се веќе дел од R2 (Sale). |
| | 560 | Атрибутите categoryId, supplierId се веќе дел од R1 (Product). |
| | 561 | |
| | 562 | U11 претставува врска меѓу продажби (saleId), нарачки (poId) и продукти (productId). |
| | 563 | |
| | 564 | '''Прашање:''' Дали оваа релација има семантичка смисла? |
| | 565 | |
| | 566 | Во реалноста: |
| | 567 | * Една продажба (saleId) е независна од една нарачка (poId) |
| | 568 | * Нивната врска е само преку productId и warehouseId |
| | 569 | |
| | 570 | '''U11 може да се декомпонира понатаму''', бидејќи атрибутите userId, customerId, warehouseId зависат само од saleId (FD10), а supplierId зависи само од poId или productId. |
| | 571 | |
| | 572 | Но бидејќи овие атрибути се веќе издвоени во R2 и R1, U11 станува редундантна и може да се отстрани бидејќи информацијата е веќе зачувана во другите релации. |
| | 573 | |
| | 574 | === Состојба после 3NF декомпозиција === |
| | 575 | |
| | 576 | ||= Релација =||= Атрибути =||= Примарен клуч =|| |
| | 577 | || '''R1''' (Product) || productId, product_name, product_description, product_sku, product_unit_price, product_reorder_level, categoryId, supplierId || productId || |
| | 578 | || '''R2''' (Sale) || saleId, sale_date_time, sale_total_amount, userId, customerId, warehouseId || saleId || |
| | 579 | || '''R3''' (!PurchaseOrder) || poId, po_order_date, po_expected_delivery_date, po_status, supplierId, warehouseId || poId || |
| | 580 | || '''R4''' (!SaleItem) || saleId, productId, saleitem_quantity, saleitem_unit_price_at_sale || {saleId, productId} || |
| | 581 | || '''R5''' (!PurchaseOrderItem) || poId, productId, poitem_quantity, poitem_unit_cost || {poId, productId} || |
| | 582 | || '''R6''' (User) || userId, user_username, user_password, user_full_name, user_email, user_role, user_is_active || userId || |
| | 583 | || '''R7''' (Customer) || customerId, customer_name, customer_email, customer_phone, customer_address || customerId || |
| | 584 | || '''R8''' (Category) || categoryId, category_name, category_description || categoryId || |
| | 585 | || '''R9''' (Supplier) || supplierId, supplier_name, supplier_contact_person, supplier_phone, supplier_email, supplier_address || supplierId || |
| | 586 | || '''R10''' (Warehouse) || warehouseId, warehouse_name, warehouse_location, warehouse_capacity || warehouseId || |
| | 587 | || '''R11''' (!WarehouseStock) || warehouseId, productId, stock_quantity_on_hand, stock_last_updated || {warehouseId, productId} || |
| | 588 | |
| | 589 | ---- |
| | 590 | |
| | 591 | == 7. BCNF Декомпозиција == |
| | 592 | |
| | 593 | === Проверка за BCNF === |
| | 594 | |
| | 595 | За секоја релација проверуваме: за секоја нетривијална FD X → Y, дали X е суперклуч? |
| | 596 | |
| | 597 | ==== R1 (Product) ==== |
| | 598 | |
| | 599 | ||= FD =||= Детерминант =||= Суперклуч? =|| |
| | 600 | || productId → сите || productId || ✓ ДА || |
| | 601 | || product_sku → сите (FD6) || product_sku || ✓ ДА (кандидат клуч) || |
| | 602 | |
| | 603 | '''R1 е во BCNF''' ✓ |
| | 604 | |
| | 605 | ==== R2 (Sale) ==== |
| | 606 | |
| | 607 | ||= FD =||= Детерминант =||= Суперклуч? =|| |
| | 608 | || saleId → сите || saleId || ✓ ДА || |
| | 609 | |
| | 610 | '''R2 е во BCNF''' ✓ |
| | 611 | |
| | 612 | ==== R3 (!PurchaseOrder) ==== |
| | 613 | |
| | 614 | ||= FD =||= Детерминант =||= Суперклуч? =|| |
| | 615 | || poId → сите || poId || ✓ ДА || |
| | 616 | |
| | 617 | '''R3 е во BCNF''' ✓ |
| | 618 | |
| | 619 | ==== R4 (!SaleItem) ==== |
| | 620 | |
| | 621 | ||= FD =||= Детерминант =||= Суперклуч? =|| |
| | 622 | || {saleId, productId} → сите || {saleId, productId} || ✓ ДА || |
| | 623 | |
| | 624 | '''R4 е во BCNF''' ✓ |
| | 625 | |
| | 626 | ==== R5 (!PurchaseOrderItem) ==== |
| | 627 | |
| | 628 | ||= FD =||= Детерминант =||= Суперклуч? =|| |
| | 629 | || {poId, productId} → сите || {poId, productId} || ✓ ДА || |
| | 630 | |
| | 631 | '''R5 е во BCNF''' ✓ |
| | 632 | |
| | 633 | ==== R6 (User) ==== |
| | 634 | |
| | 635 | ||= FD =||= Детерминант =||= Суперклуч? =|| |
| | 636 | || userId → сите || userId || ✓ ДА || |
| | 637 | || user_username → сите (FD2) || user_username || ✓ ДА (кандидат клуч) || |
| | 638 | || user_email → сите (FD3) || user_email || ✓ ДА (кандидат клуч) || |
| | 639 | |
| | 640 | '''R6 е во BCNF''' ✓ |
| | 641 | |
| | 642 | ==== R7 (Customer) ==== |
| | 643 | |
| | 644 | ||= FD =||= Детерминант =||= Суперклуч? =|| |
| | 645 | || customerId → сите || customerId || ✓ ДА || |
| | 646 | |
| | 647 | '''R7 е во BCNF''' ✓ |
| | 648 | |
| | 649 | ==== R8 (Category) ==== |
| | 650 | |
| | 651 | ||= FD =||= Детерминант =||= Суперклуч? =|| |
| | 652 | || categoryId → сите || categoryId || ✓ ДА || |
| | 653 | |
| | 654 | '''R8 е во BCNF''' ✓ |
| | 655 | |
| | 656 | ==== R9 (Supplier) ==== |
| | 657 | |
| | 658 | ||= FD =||= Детерминант =||= Суперклуч? =|| |
| | 659 | || supplierId → сите || supplierId || ✓ ДА || |
| | 660 | |
| | 661 | '''R9 е во BCNF''' ✓ |
| | 662 | |
| | 663 | ==== R10 (Warehouse) ==== |
| | 664 | |
| | 665 | ||= FD =||= Детерминант =||= Суперклуч? =|| |
| | 666 | || warehouseId → сите || warehouseId || ✓ ДА || |
| | 667 | |
| | 668 | '''R10 е во BCNF''' ✓ |
| | 669 | |
| | 670 | ==== R11 (!WarehouseStock) ==== |
| | 671 | |
| | 672 | ||= FD =||= Детерминант =||= Суперклуч? =|| |
| | 673 | || {warehouseId, productId} → сите || {warehouseId, productId} || ✓ ДА || |
| | 674 | |
| | 675 | '''R11 е во BCNF''' ✓ |
| | 676 | |
| | 677 | '''Заклучок: Сите релации се во BCNF.''' |
| | 678 | |
| | 679 | ---- |
| | 680 | |
| | 681 | == 8. Финален резултат и дискусија == |
| | 682 | |
| | 683 | === 8.1 Финални релации === |
| | 684 | |
| | 685 | {{{ |
| | 686 | User(userId, username, password, full_name, email, role, is_active) |
| | 687 | - Примарен клуч: userId |
| | 688 | - Кандидат клучеви: userId, username, email |
| | 689 | - Надворешни клучеви: - |
| | 690 | |
| | 691 | Customer(customerId, name, email, phone, address) |
| | 692 | - Примарен клуч: customerId |
| | 693 | - Кандидат клучеви: customerId |
| | 694 | - Надворешни клучеви: - |
| | 695 | |
| | 696 | Category(categoryId, name, description) |
| | 697 | - Примарен клуч: categoryId |
| | 698 | - Кандидат клучеви: categoryId |
| | 699 | - Надворешни клучеви: - |
| | 700 | |
| | 701 | Supplier(supplierId, name, contact_person, phone, email, address) |
| | 702 | - Примарен клуч: supplierId |
| | 703 | - Кандидат клучеви: supplierId |
| | 704 | - Надворешни клучеви: - |
| | 705 | |
| | 706 | Warehouse(warehouseId, name, location, capacity) |
| | 707 | - Примарен клуч: warehouseId |
| | 708 | - Кандидат клучеви: warehouseId |
| | 709 | - Надворешни клучеви: - |
| | 710 | |
| | 711 | Product(productId, name, description, sku, unit_price, reorder_level, categoryId, supplierId) |
| | 712 | - Примарен клуч: productId |
| | 713 | - Кандидат клучеви: productId, sku |
| | 714 | - Надворешни клучеви: categoryId → Category, supplierId → Supplier |
| | 715 | |
| | 716 | Sale(saleId, date_time, total_amount, userId, customerId, warehouseId) |
| | 717 | - Примарен клуч: saleId |
| | 718 | - Кандидат клучеви: saleId |
| | 719 | - Надворешни клучеви: userId → User, customerId → Customer, warehouseId → Warehouse |
| | 720 | |
| | 721 | PurchaseOrder(poId, order_date, expected_delivery_date, status, supplierId, warehouseId) |
| | 722 | - Примарен клуч: poId |
| | 723 | - Кандидат клучеви: poId |
| | 724 | - Надворешни клучеви: supplierId → Supplier, warehouseId → Warehouse |
| | 725 | |
| | 726 | SaleItem(saleId, productId, quantity, unit_price_at_sale) |
| | 727 | - Примарен клуч: {saleId, productId} |
| | 728 | - Кандидат клучеви: {saleId, productId} |
| | 729 | - Надворешни клучеви: saleId → Sale, productId → Product |
| | 730 | |
| | 731 | PurchaseOrderItem(poId, productId, quantity, unit_cost) |
| | 732 | - Примарен клуч: {poId, productId} |
| | 733 | - Кандидат клучеви: {poId, productId} |
| | 734 | - Надворешни клучеви: poId → PurchaseOrder, productId → Product |
| | 735 | |
| | 736 | WarehouseStock(warehouseId, productId, quantity_on_hand, last_updated) |
| | 737 | - Примарен клуч: {warehouseId, productId} |
| | 738 | - Кандидат клучеви: {warehouseId, productId} |
| | 739 | - Надворешни клучеви: warehouseId → Warehouse, productId → Product |
| | 740 | }}} |
| | 741 | |
| | 742 | === 8.2 Дискусија на разликите од моделот во Фаза P2 === |
| | 743 | |
| | 744 | Преку процесот на нормализација, тргнувајќи од една универзална релација, дојдовме до '''истите 11 релации''' како во концептуалниот модел од Фаза P2. Ова покажува дека: |
| | 745 | |
| | 746 | 1. '''Концептуалниот модел беше правилно дизајниран''' - ентитетите и нивните атрибути беа логички групирани. |
| | 747 | |
| | 748 | 2. '''Процесот на нормализација го потврди дизајнот''' - секоја декомпозиција базирана на функционалните зависности резултираше со релација која одговара на ентитет од оригиналниот модел. |
| | 749 | |
| | 750 | 3. '''Клучот на универзалната релација {saleId, poId, productId}''' беше идентификуван преку анализа на функционалните зависности и класификација лева/десна страна. |
| | 751 | |
| | 752 | 4. '''Сите релации се во BCNF''' - нема функционална зависност каде детерминантот не е суперклуч. |
| | 753 | |
| | 754 | 5. '''Декомпозицијата е lossless''' - оригиналната информација може да се реконструира. |
| | 755 | |
| | 756 | 6. '''Сите функционални зависности се зачувани''' - секоја FD е зачувана во барем една од финалните релации. |