wiki:Normalization

Нормализација

Денормализирана форма

Форма во кој има една табела во која се внесени сите ентитети и нивни релации помеѓу себе, без никакви правила

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@… $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@… $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@… $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@… $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@… $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@… $2y$10$rcqLdIzMcYfmGFWCP3kix.JNTOzjIp0xVehMd11wzaanAXUDFLQMu
2 anita anita@… $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 }

Бидејќи {user_id} → {user_name, email, password}, а user_id ∈ (R ∩ R1), следи дека (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 }

Бидејќи {transaction_account_id} → {account_name, balance, user_id}, а transaction_account_id ∈ (R1.1 ∩ R2), следи дека (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 }

Бидејќи {transaction_id} → {transaction_name, amount, net_amount, date, tag_id, tag_name}, а transaction_id ∈ (R2.1 ∩ R3), следи дека (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 }

Бидејќи {transaction_breakdown_id} → {transaction_id, transaction_account_id, spent_amount, earned_amount}, а transaction_breakdown_id ∈ (R3.1 ∩ R4), следи дека (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, за поедноставување, се разгледува случај со еден таг по трансакција, оваа претпоставка се отстранува при премин во 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@… $2y$10$rcqLdIzMcYfmGFWCP3kix.JNTOzjIp0xVehMd11wzaanAXUDFLQMu
2 anita anita@… $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 }

Бидејќи {transaction_id} → {transaction_name, amount, net_amount, date}, а transaction_id ∈ (R3 ∩ R3.2), следи дека (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 }

Бидејќи {tag_id} → {tag_name}, а tag_id ∈ (R3.3 ∩ R3.4), следи дека (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 тест се елиминираат сите аномалии, се обезбедува логичка коректност и се добива шема погодна за имплементација во релациона датабаза.

Финални табели:

USER
user_id user_name email password
1 james james@… $2y$10$rcqLdIzMcYfmGFWCP3kix.JNTOzjIp0xVehMd11wzaanAXUDFLQMu
2 anita anita@… $2y$10$CsSsqA.FFhBR/TWCZCUWYOPxYA.HmGb7ULQPgJGhv3vQS2xRqluYm

TRANSACTION_ACCOUNT
transaction_account_id account_name balance user_id
1 credit card 5000 1
2 cash 1200 1
3 debit card 3200 2

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

TAG
tag_id tag_name
1 bills
2 food
3 beauty

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

TAG_ASSIGNED_TO_TRANSACTION
transaction_id tag_id
1 1
2 2
3 3

Верзија 1

Last modified 4 days ago Last modified on 12/30/25 11:48:54
Note: See TracWiki for help on using the wiki.