= Нормализација == Денормализирана форма Форма во кој има една табела во која се внесени сите ентитети и нивни релации помеѓу себе, без никакви правила ||= //user_id =||= //user_name =||= //email =||= //password =||= //transaction_account_id =||= //account_name =||= //balance =||= //transaction_id =||= //transaction_name =||= //amount =||= //net_amount =||= //date =||= //transaction_breakdown_id =||= //spent_amount =||= //earned_amount =||= //tag_id =||= //tag_name =|| || 1|| james || james@fein.com || $2y$10$rcqLdIzMcYfmGFWCP3kix.JNTOzjIp0xVehMd11wzaanAXUDFLQMu || 1, 2 || credit card, cash || 5000, 1200 || 1, 2 || electricity bill, burger || -1800, -800 || -1800, -200 || 18:03:25 Sep 26, 2025, 12:43:00 Sep 26, 2025 || 1,2 || -1800, -800 || 0, 600 || 1, 2 || bills, food || || 2|| anita || anita@fein.com || $2y$10$CsSsqA.FFhBR/TWCZCUWYOPxYA.HmGb7ULQPgJGhv3vQS2xRqluYm || 1 || debit card || 3200 || 1 || nail polish || -200 || -200 || 19:31:32 Sep 12 2025 || 1 || -200 || 0 || 1 || beauty || Во денормализираната табела постојат следниве основни функционални зависности: - {user_id} → {user_name, email, password} - {transaction_account_id} → {account_name, balance, user_id} - {transaction_id} → {transaction_name, amount, net_amount, date} - {transaction_breakdown_id} → {transaction_id, transaction_account_id, spent_amount, earned_amount} - {tag_id} → {tag_name} == Прва нормална форма Форма во која повторно има една табела, но овојпат, таа е ограничена со следниве работи: - Подредувањето на редовите не претставува никакво значење - Во ќелиите на секоја колона има по една вредност - Не се мешаат типови на податоци во една ќелија - Табелата има примарен композитен клуч(user_id, transaction_id, transaction_account_id, transaction_breakdown_id) - Нема повторувачки групи, сите што биле во денормализираната форма сега се во посебен ред ==== R ||= //user_id =||= //user_name =||= //email =||= //password =||= //transaction_account_id =||= //account_name =||= //balance =||= //transaction_id =||= //transaction_name =||= //amount =||= //net_amount =||= //date =||= //transaction_breakdown_id =||= //spent_amount =||= //earned_amount =||= //tag_id =||= //tag_name =|| || 1|| james || james@fein.com || $2y$10$rcqLdIzMcYfmGFWCP3kix.JNTOzjIp0xVehMd11wzaanAXUDFLQMu || 1|| credit card || 5000|| 1|| electricity bill || -1800|| -1800|| 18:03:25 Sep 26, 2025 || 1|| -1800|| 0|| 1|| bills || || 1|| james || james@fein.com || $2y$10$rcqLdIzMcYfmGFWCP3kix.JNTOzjIp0xVehMd11wzaanAXUDFLQMu || 2|| cash || 1200|| 2|| burger || -800|| -200|| 12:43:00 Sep 26, 2025 || 2|| -800|| 600|| 2|| food || || 2|| anita || anita@fein.com || $2y$10$CsSsqA.FFhBR/TWCZCUWYOPxYA.HmGb7ULQPgJGhv3vQS2xRqluYm || 3|| debit card || 3200|| 3|| nail polish || -200|| -200|| 19:31:32 Sep 12 2025 || 3|| -200|| 0|| 3|| beauty || ==== Избор на примарен клуч во 1NF Во првата нормална форма е дефиниран композитен примарен клуч: //(user_id, transaction_id, transaction_account_id, transaction_breakdown_id)// tag_id може да биде вклучен во композитниот примарен клуч во 1NF бидејќи 1NF само ограничува по атомност и уникатност на ред, но не треба да биде вклучен бидејќи ја нарушува минималноста на клучот, додава парцијални зависности и погрешно ја моделира many-to-many релацијата. Затоа, tag_id мора да биде одделен во посебна релација во погорните нормални форми. Овој клуч е избран бидејќи eдинствено го идентификува секој ред, ги опфаќа сите повторувачки групи од денормализираната форма и не постои подмножество од овој клуч кое може самостојно да го идентификува редот. === Премин од 1NF во 2NF Проблем се делумните зависности, во 1NF постојат атрибути кои зависат само од дел од примарниот клуч, на пример: - {user_id} → {user_name, email, password} - {transaction_id} → {transaction_name, amount, net_amount, date} Ова претставува кршење на втората нормална форма, решение за тоа е декомпозиција, односно секоја група на атрибути која зависи од дел од композитниот клуч се издвојува во посебна табела каде тој дел станува примарен клуч. Пример: - USER(user_id PK, user_name, email, password) == Втора нормална форма Форма која ги следи овие правила: - Веќе е во прва нормална форма - Секој од атрибутите кој не е клуч, зависи од целосниот примарен клуч - со тоа се спречуваат аномалии на внесување, бришење и менување ==== R1 { user_id } → {user_name, email, password} ||= USER =||= =||= =||= =|| ||= //user_id =||= //user_name =||= //email =||= //password =|| || 1 || james || james@fein.com || $2y$10$rcqLdIzMcYfmGFWCP3kix.JNTOzjIp0xVehMd11wzaanAXUDFLQMu || || 2 || anita || anita@fein.com || $2y$10$CsSsqA.FFhBR/TWCZCUWYOPxYA.HmGb7ULQPgJGhv3vQS2xRqluYm || ====== Lossless join test R: ||= //user_id =||= //user_name =||= //email =||= //password =||= //transaction_account_id =||= //account_name =||= //balance =||= //transaction_id =||= //transaction_name =||= //amount =||= //net_amount =||= //date =||= //transaction_breakdown_id =||= //spent_amount =||= //earned_amount =||= //tag_id =||= //tag_name =|| R1: ||= //user_id =||= //user_name =||= //email =||= //password =|| R ∩ R1 = { user_id, user_name, email, password } R ∩ R1 → R1 ⇒ Декомпозицијата е lossless R1.1 = R - { user_name, email, password } ==== R2 {transaction_account_id} → {account_name, balance, user_id} ||= TRANSACTION_ACCOUNT =||= =||= =||= || ||= //transaction_account_id =||= //account_name =||= //balance =||= //user_id || || 1 || credit card || 5000 || 1 || || 2 || cash || 1200 || 1 || || 3 || debit card || 3200 || 2 || ====== Lossless join test R1.1: ||= //user_id =||= //transaction_account_id =||= //account_name =||= //balance =||= //transaction_id =||= //transaction_name =||= //amount =||= //net_amount =||= //date =||= //transaction_breakdown_id =||= //spent_amount =||= //earned_amount =||= //tag_id =||= //tag_name =|| R2: ||= //transaction_account_id =||= //account_name =||= //balance =||= //user_id || R1.1 ∩ R2 = { transaction_account_id, account_name, balance, user_id } R1.1 ∩ R2 → R2 ⇒ Декомпозицијата е lossless R2.1 = R1.1 - { account_name, balance, user_id } ==== R3 {transaction_id} → {transaction_name, amount, net_amount, date, tag_id} {transaction_id} → {tag_id} → {tag_name} ||= TRANSACTION_WITH_TAG_NAME =||= =||= =||= =||= =||= =||= =|| ||= //transaction_id =||= //transaction_name =||= //amount =||= //net_amount =||= //date =||= //tag_id =||= //tag_name =|| || 1 || electricity bill || -1800 || -1800 || 18:03:25 Sep 26, 2025 || 1 || bills || || 2 || burger || -800 || -200 || 12:43:00 Sep 26, 2025 || 2 || food || || 3 || nail polish || -200 || -200 || 19:31:32 Sep 12 2025 || 3 || beauty || ====== Lossless join test R2.1: ||= //transaction_account_id =||= //transaction_id =||= //transaction_name =||= //amount =||= //net_amount =||= //date =||= //transaction_breakdown_id =||= //spent_amount =||= //earned_amount =||= //tag_id =||= //tag_name =|| R3: ||= //transaction_id =||= //transaction_name =||= //amount =||= //net_amount =||= //date =||= //tag_id =||= //tag_name =|| R2.1 ∩ R3 = { transaction_id, transaction_name, amount, net_amount, date, tag_id, tag_name } R2.1 ∩ R3 → R3 ⇒ Декомпозицијата е lossless R3.1 = R2.1 - { transaction_name, amount, net_amount, date, tag_id, tag_name } ==== R4 {transaction_breakdown_id} → {transaction_id, transaction_account_id, spent_amount, earned_amount} ||= TRANSACTION_BREAKDOWN =||= =||= =||= =||= =|| ||= //transaction_breakdown_id =||= //transaction_id =||= //transaction_account_id =||= //spent_amount =||= //earned_amount =|| || 1 || 1 || 1 || -1800 || 0 || || 2 || 2 || 2 || -800 || 600 || || 3 || 3 || 3 || -200 || 0 || ====== Lossless join test R3.1: ||= //transaction_account_id =||= //transaction_id =||= //transaction_breakdown_id =||= //spent_amount =||= //earned_amount =|| R4: ||= //transaction_breakdown_id =||= //transaction_id =||= //transaction_account_id =||= //spent_amount =||= //earned_amount =|| R3.1 ∩ R4 = { transaction_breakdown_id, transaction_id, transaction_account_id, spent_amount, earned_amount } R3.1 ∩ R4 → R4 ⇒ Декомпозицијата е lossless R4.1 = R3.1 - { transaction_breakdown_id, transaction_id, transaction_account_id, spent_amount, earned_amount } R4.1 = ∅ === Премин од 2NF во 3NF Проблем се транзитивните зависности, кои во 2NF сè уште постојат, на пример: - {transaction_id} → {tag_id} → {tag_name} Ова значи дека некои атрибути кои не се клучеви зависат од други атрибути кои исто така не се клучеви, што ја крши 3NF. Решение за ова е отстранување на транзитивноста, односно секој атрибут кој не зависи директно од примарниот клуч се издвојува во нова табела. Пример: - TAG(tag_id PK, tag_name) - TAG_ASSIGNED_TO_TRANSACTION(transaction_id PK, tag_id PK) == Трета нормална форма Формава ги следи овие правила: - Веќе е во втора нормална форма - Се отргнуваат транзитивните функционални зависности, со тоа се овозможува сите атрибути кои не се клучеви да зависат само од примарниот клуч и од никој друг атрибут ==== R1 {user_id} → {user_name, email, password} ||= USER =||= =||= =||= =|| ||= //user_id =||= //user_name =||= //email =||= //password =|| || 1 || james || james@fein.com || $2y$10$rcqLdIzMcYfmGFWCP3kix.JNTOzjIp0xVehMd11wzaanAXUDFLQMu || || 2 || anita || anita@fein.com || $2y$10$CsSsqA.FFhBR/TWCZCUWYOPxYA.HmGb7ULQPgJGhv3vQS2xRqluYm || ==== R2 {transaction_account_id} → {account_name, balance, user_id} ||= TRANSACTION_ACCOUNT =||= =||= =||= =|| ||= //transaction_account_id =||= //account_name =||= //balance =||= //user_id =|| || 1 || credit card || 5000 || 1 || || 2 || cash || 1200 || 1 || || 3 || debit card || 3200 || 2 || ==== R3.2 {transaction_id} → {transaction_name, amount, net_amount, date} ||= TRANSACTION =||= =||= =||= =||= =|| ||= //transaction_id =||= //transaction_name =||= //amount =||= //net_amount =||= //date =|| || 1 || electricity bill || -1800 || -1800 || 18:03:25 Sep 26, 2025 || || 2 || burger || -800 || -200 || 12:43:00 Sep 26, 2025 || || 3 || nail polish || -200 || -200 || 19:31:32 Sep 12 2025 || ====== Lossless join test R3: ||= //transaction_id =||= //transaction_name =||= //amount =||= //net_amount =||= //date =||= //tag_id =||= //tag_name =|| R3.2: ||= //transaction_id =||= //transaction_name =||= //amount =||= //net_amount =||= //date =|| R3 ∩ R3.2 = { transaction_id, transaction_name, amount, net_amount, date } R3 ∩ R3.2 → R3.2 ⇒ Декомпозицијата е lossless R3.3 = R3 - { transaction_id, transaction_name, amount, net_amount, date } ==== R3.4 {tag_id} → {tag_name} ||= TAG =||= =|| ||= //tag_id =||= //tag_name =|| || 1 || bills || || 2 || food || || 3 || beauty || ====== Lossless join test R3.3: ||= //tag_id =||= //tag_name =|| R3.4: ||= //tag_id =||= //tag_name =|| R3.3 ∩ R3.4 = { tag_id, tag_name } R3.3 ∩ R3.4 → R3.4 ⇒ Декомпозицијата е lossless R3.5 = R3.3 - { tag_id, tag_name } R3.5 = ∅ ==== R4 {transaction_breakdown_id} → {transaction_id, transaction_account_id, spent_amount, earned_amount} ||= TRANSACTION_BREAKDOWN =||= =||= =||= =||= =|| ||= //transaction_breakdown_id =||= //transaction_id =||= //transaction_account_id =||= //spent_amount =||= //earned_amount =|| || 1 || 1 || 1 || -1800 || 0 || || 2 || 2 || 2 || -800 || 600 || || 3 || 3 || 3 || -200 || 0 || ==== R5 {transaction_id, tag_id} → {} ||= TAG_ASSIGNED_TO_TRANSACTION =||= =|| ||= //transaction_id =||= //tag_id =|| || 1 || 1 || || 2 || 2 || || 3 || 3 || ==== Избор на примарни клучеви во 3NF По правилото дека секој примарен клуч мора минимално и единствено да го идентификува редот. Примери: - USER.user_id — природен идентификатор - TRANSACTION.transaction_id — сурогатен клуч - TAG_ASSIGNED_TO_TRANSACTION(transaction_id, tag_id) — композитен клуч за M:N релација - TRANSACTION_BREAKDOWN.transaction_breakdown_id — сурогатен клуч ==== Третата нормална форма погоре исто така е и BCNF === Сите функционални зависности можат да се проверат локално во поединечни табели, без потреба од JOIN операции, oва значи дека шемата е dependency-preserving, lossless-join и е во BCNF. == Заклучок Со примената на формална анализа на функционални зависности, правилен избор на примарни клучеви и проверка со lossless-join тест се елиминираат сите аномалии, се обезбедува логичка коректност и се добива шема погодна за имплементација во релациона датабаза. == [wiki:NormalizationVer1 Верзија 1]