| Version 10 (modified by , 9 days ago) ( diff ) |
|---|
Нормализација
Денормализирана форма
Форма во кој има една табела во која се внесени сите ентитети и нивни релации помеѓу себе, без никакви правила
| user_id | user_name | 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)
- Нема повторувачки групи, сите што биле во денормализираната форма сега се во посебен ред
| user_id | user_name | 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)
Овој клуч е избран бидејќи 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)
Втора нормална форма
Форма која ги следи овие правила:
- Веќе е во прва нормална форма
- Секој од атрибутите кој не е клуч, зависи од целосниот примарен клуч - со тоа се спречуваат аномалии на внесување, бришење и менување
1
{ user_id } → {user_name, email, password}
| USER | |||
|---|---|---|---|
| user_id | user_name | password | |
| 1 | james | james@… | $2y$10$rcqLdIzMcYfmGFWCP3kix.JNTOzjIp0xVehMd11wzaanAXUDFLQMu |
| 2 | anita | anita@… | $2y$10$CsSsqA.FFhBR/TWCZCUWYOPxYA.HmGb7ULQPgJGhv3vQS2xRqluYm |
2
{transaction_account_id} → {account_name, balance, user_id}
{transaction_account_id} → {user_id} → {user_name, email}
| TRANSACTION_ACCOUNT_WITH_USER_DATA | |||||
|---|---|---|---|---|---|
| transaction_account_id | account_name | balance | user_id | user_name | |
| 1 | credit card | 5000 | 1 | james | james@… |
| 2 | cash | 1200 | 1 | james | james@… |
| 3 | debit card | 3200 | 2 | anita | anita@… |
3
{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 |
4
{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 |
Премин од 2NF во 3NF
Проблем се транзитивните зависности, кои во 2NF сè уште постојат, на пример:
- {transaction_id} → {tag_id} → {tag_name}
- {transaction_account_id} → {user_id} → {user_name, email}
Ова значи дека некои атрибути кои не се клучеви зависат од други атрибути кои исто така не се клучеви, што ја крши 3NF.
Решение за ова е отстранување на транзитивноста, односно секој атрибут кој не зависи директно од примарниот клуч се издвојува во нова табела. Пример:
- TAG(tag_id PK, tag_name)
- TAG_ASSIGNED_TO_TRANSACTION(transaction_id PK, tag_id PK)
Трета нормална форма
Формава ги следи овие правила:
- Веќе е во втора нормална форма
- Се отргнуваат транзитивните функционални зависности, со тоа се овозможува сите атрибути кои не се клучеви да зависат само од примарниот клуч и од никој друг атрибут
1
{user_id} → {user_name, email, password}
| USER | |||
|---|---|---|---|
| user_id | user_name | password | |
| 1 | james | james@… | $2y$10$rcqLdIzMcYfmGFWCP3kix.JNTOzjIp0xVehMd11wzaanAXUDFLQMu |
| 2 | anita | anita@… | $2y$10$CsSsqA.FFhBR/TWCZCUWYOPxYA.HmGb7ULQPgJGhv3vQS2xRqluYm |
2
{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 |
3
{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 |
4
{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 |
5
{tag_id} → {tag_name}
| TAG | |
|---|---|
| tag_id | tag_name |
| 1 | bills |
| 2 | food |
| 3 | beauty |
6
{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
Lossless Join Test
Секоја декомпозиција направена при нормализацијата е lossless, односно со JOIN операција може целосно да се реконструира оригиналната релација без губење на информации.
Декомпозицијата на релација R(X) во R1(A) и R2(B) е lossless ако важи:
- (A ∩ B) → A или (A ∩ B) → B
1
TRANSACTION_ACCOUNT_WITH_USER_DATA
- R(transaction_account_id, account_name, balance, user_id, user_name, email)
- R1(transaction_account_id, account_name, balance, user_id)
- R2(user_id, user_name, email)
R1 ∩ R2 = {user_id}
- {transaction_account_id} → {user_id}
- {user_id} → {user_name, email}
(user_id) → R2 ⇒ Декомпозицијата е lossless
2
TRANSACTION_WITH_TAG_NAME
- R(transaction_id, transaction_name, amount, net_amount, date, tag_id, tag_name)
- R1(transaction_id, transaction_name, amount, net_amount, date, tag_id)
- R2(tag_id, tag_name)
R1 ∩ R2 = {tag_id}
- {tag_id} → {tag_name}
(tag_id) → R2 ⇒ Декомпозицијата е lossless
3
TRANSACTION → TAG_ASSIGNED_TO_TRANSACTION
- R(transaction_id, transaction_name, amount, net_amount, date, tag_id)
- R1(transaction_id, transaction_name, amount, net_amount, date)
- R2(transaction_id, tag_id)
R1 ∩ R2 = {transaction_id}
- {transaction_id} → {transaction_name, amount, net_amount, date}
(transaction_id) → R1 ⇒ Декомпозицијата е lossless
4
TRANSACTION_ACCOUNT_WITH_USER_DATA → TRANSACTION_ACCOUNT + USER
- R(transaction_account_id, account_name, balance, user_id, user_name, email)
- R1(transaction_account_id, account_name, balance, user_id)
- R2(user_id, user_name, email)
R1 ∩ R2 = {user_id}
- {user_id} → {user_name, email}
(user_id) → R2 ⇒ Декомпозицијата е lossless
5
TAG
- R(tag_id, tag_name)
- R1(tag_id, tag_name)
(Нема реална декомпозиција – табелата е веќе атомска) ⇒ Тривијално lossless
6
TRANSACTION_BREAKDOWN
- R(transaction_breakdown_id, transaction_id, transaction_account_id, spent_amount, earned_amount)
Не е извршена декомпозиција, примарниот клуч функционално ги определува сите атрибути ⇒ Нема потреба од lossless join тест
Сите функционални зависности можат да се проверат локално во поединечни табели, без потреба од JOIN операции, oва значи дека шемата е dependency-preserving, lossless-join и е во BCNF.
Заклучок
Со примената на формална анализа на функционални зависности, правилен избор на примарни клучеви и проверка со lossless-join тест се елиминираат сите аномалии, се обезбедува логичка коректност и се добива шема погодна за имплементација во релациона датабаза.
