| Version 13 (modified by , 5 days ago) ( diff ) |
|---|
Нормализација
Де-нормализирана база на податоци
Се тргнува од една глобална, де-нормализирана релација што ги содржи атрибутите од целиот модел. Атрибутите со исто логичко значење но различно потекло се именуваат со префикс за да се избегне колизија на имиња во рамките на глобалната релација (на пример training_weight vs weight_current, training_date vs daily_intake_date):
R = { user_id, email, username, password, training_gender, training_age, training_weight, training_id, training_user_id, training_date, training_type, training_duration, training_calories, asset_id, asset_ticker_symbol, asset_buy_price, asset_buy_date, asset_quantity, weight_current, weight_height, weight_goal_weight, weight_goal_calories, daily_intake_id, daily_intake_date, daily_intake_calories, discipline_user_id, custom_tracking_id, custom_tracking_name, task_id, task_name, task_is_finished, daily_completion_id, daily_completion_date, daily_completion_procent, finance_spending_budget, finance_saving_budget, finance_investing_budget, finance_donation_budget, finance_credit, income_id, income_date, income_amount }
Напомена: Атрибутите num_tasks и tasks се отстранети уште од првата нормализација бидејќи се изведени/не-атомски вредности и не припаѓаат на релацискиот модел. Атрибутот training_user_id е реална FK колона во TRAINING_SESSIONS (References TRAINING_USERS.user_id) и затоа се наоѓа во R. Атрибутот discipline_user_id е реална FK колона во TASKS (References DISCIPLINE_USERS.user_id) и исто така се наоѓа во R. Сите профилски табели (TRAINING_USERS, WEIGHT_USERS, INVESTOR_USERS, DISCIPLINE_USERS, FINANCE_USERS) го користат user_id и како примарен и како надворешен клуч — затоа не постојат одделни атрибути training_user_id, weight_user_id итн. во тие табели.
Функционални зависности
Канонски покривач на функционалните зависности:
- FD1:
user_id -> email, username, password - FD2:
user_id -> training_gender, training_age, training_weight - FD3:
training_user_id -> user_id - FD4:
training_id -> training_user_id, training_date, training_type, training_duration, training_calories - FD5:
user_id -> weight_current, weight_height, weight_goal_weight, weight_goal_calories - FD6:
daily_intake_id -> user_id, daily_intake_date, daily_intake_calories - FD7:
asset_id -> user_id, asset_ticker_symbol, asset_buy_price, asset_buy_date, asset_quantity - FD8:
discipline_user_id -> user_id - FD9:
custom_tracking_id -> user_id, custom_tracking_name - FD10:
task_id -> discipline_user_id, custom_tracking_id, task_name, task_is_finished - FD11:
daily_completion_id -> user_id, daily_completion_date, daily_completion_procent - FD12:
(task_id, daily_completion_id) -> /(нема дополнителни атрибути — само врска) - FD13:
user_id -> finance_spending_budget, finance_saving_budget, finance_investing_budget, finance_donation_budget, finance_credit - FD14:
income_id -> user_id, income_date, income_amount
Кандидат клучеви и примарен клуч
Пресметување на затворања (closures)
За да се најдат кандидат клучевите, пресметуваме затворање за секој атрибут (или комбинација) и проверуваме дали го покрива целиот R.
Атрибути кои се појавуваат само на десна страна (никогаш детерминанти):
training_gender,training_age,training_weight,training_date,training_type,training_duration,training_caloriesasset_ticker_symbol,asset_buy_price,asset_buy_date,asset_quantityweight_current,weight_height,weight_goal_weight,weight_goal_caloriesdaily_intake_date,daily_intake_caloriescustom_tracking_name,task_name,task_is_finisheddaily_completion_date,daily_completion_procentfinance_spending_budget,finance_saving_budget,finance_investing_budget,finance_donation_budget,finance_creditincome_date,income_amountemail,username,password
Атрибути кои се појавуваат на лева и десна страна:
user_id, training_user_id, discipline_user_id, custom_tracking_id
Овие атрибути не можат да бидат дел од кандидат клуч бидејќи не детерминираат ништо надвор од себе.
Атрибути кои се само на лева страна (мора да бидат во секој кандидат клуч):
training_id,asset_id,daily_intake_id,task_id,daily_completion_id,income_id
Затворање на минималниот суперклуч
Покривачи:
- training_id+ -> {training_id, training_user_id, training_date, training_type, training_duration, training_calories} via FD4 -> {user_id} via FD3 -> {email, username, password} via FD1 -> {training_gender, training_age, training_weight} via FD2 -> {weight_current, weight_height, weight_goal_weight, weight_goal_calories} via FD5 -> {finance_spending_budget, ..., finance_credit} via FD13 -> Не ги содржи сите атрибути
- asset_id+ -> {asset_id, user_id, asset_ticker_symbol, asset_buy_price, asset_buy_date, asset_quantity} via FD7 -> {email, username, password, training_gender, ..., weight_current, ..., finance_...} via FD1,FD2,FD5,FD13 -> Не ги содржи сите атрибути
- daily_intake_id+ -> {daily_intake_id, user_id, daily_intake_date, daily_intake_calories} via FD6 -> {email, username, password, ...} via FD1,FD2,FD5,FD13 -> Не ги содржи сите атрибути
- task_id+ -> {task_id, discipline_user_id, custom_tracking_id, task_name, task_is_finished} via FD10 -> {user_id} via FD8 -> {user_id, custom_tracking_name} via FD9 -> {email, ...} via FD1,FD2,FD5,FD13 -> Не ги содржи сите атрибути
- daily_completion_id+ -> {daily_completion_id, user_id, daily_completion_date, daily_completion_procent} via FD11 -> {email, ...} via FD1,FD2,FD5,FD13 -> Не ги содржи сите атрибути
- income_id+ -> {income_id, user_id, income_date, income_amount} via FD14 -> {email, ...} via FD1,FD2,FD5,FD13 -> Не ги содржи сите атрибути
Бидејќи атрибутите кои се наоѓаат само на левата страна не може да се изведат на никој друг начин, тие мора да бидат дел од примарниот клуч.
{training_id, asset_id, daily_intake_id, task_id, daily_completion_id, income_id}+ -> {training_id, training_user_id, training_date, training_type, training_duration, training_calories, asset_id, user_id, asset_ticker_symbol, asset_buy_price, asset_buy_date, asset_quantity, email, username, password, training_gender, training_age, training_weight, weight_current, weight_height, weight_goal_weight, weight_goal_calories, daily_intake_id, daily_intake_date, daily_intake_calories, discipline_user_id, custom_tracking_id, custom_tracking_name, task_id, task_name, task_is_finished, daily_completion_id, daily_completion_date, daily_completion_procent, finance_spending_budget, finance_saving_budget, finance_investing_budget, finance_donation_budget, finance_credit, income_id, income_date, income_amount}
Покривачот составен од сите атрибути кои се наоѓаат само од левата страна ги содржи сите атрибути на глобалната релација и претставува минимален суперклуч, па затоа го избираме за примарен клуч.
Избран примарен клуч: К = {training_id, asset_id, daily_intake_id, task_id, daily_completion_id, income_id}
Минималност: Отстранувањето на кој било атрибут од K резултира во непокриени атрибути (на пример, без training_id не можеме да добиеме training_date, training_type итн.), па K е минимален.
Проверка за 1НФ
Бидејќи релацијата не содржи повеќевредносни атрибути, неуникатни атрибути, вредности од различен домен за еден атрибут, а притоа секоја редица е уникатно определена преку примарниот клуч, ја задоволува 1НФ.
Проверка за 2НФ
2НФ бара: релацијата е во 1НФ и нема парцијална зависност — секој не-клучен атрибут зависи од целиот примарен клуч, не само од дел од него.
Бидејќи секој атрибут зависи само од еден дел на примарниот клуч (на пример, training_date зависи само од training_id, а не од целиот K), релацијата не ја задоволува 2НФ.
Групирање на атрибутите според клучот од кој зависат:
- training_id -> training_user_id, training_date, training_type, training_duration, training_calories
- asset_id -> user_id, asset_ticker_symbol, asset_buy_price, asset_buy_date, asset_quantity
- daily_intake_id -> user_id, daily_intake_date, daily_intake_calories
- task_id -> discipline_user_id, custom_tracking_id, task_name, task_is_finished
- daily_completion_id -> user_id, daily_completion_date, daily_completion_procent
- income_id -> user_id, income_date, income_amount
Декомпозиција по 2НФ
- TrainingSessions(training_id, training_user_id, training_date, training_type, training_duration, training_calories)
R1 = R - {training_user_id, training_date, training_type, training_duration, training_calories}
R1 = {user_id, email, username, password, training_gender, training_age, training_weight, training_id, asset_id, asset_ticker_symbol, asset_buy_price, asset_buy_date, asset_quantity, weight_current, weight_height, weight_goal_weight, weight_goal_calories, daily_intake_id, daily_intake_date, daily_intake_calories, discipline_user_id, custom_tracking_id, custom_tracking_name, task_id, task_name, task_is_finished, daily_completion_id, daily_completion_date, daily_completion_procent, finance_spending_budget, finance_saving_budget, finance_investing_budget, finance_donation_budget, finance_credit, income_id, income_date, income_amount}
Lossless join: Релацијата може да се реконструира преку join со training_id.
Dependency preservation: FD4 е сочувана преку новата релација TrainingSessions. FD3 (training_user_id -> user_id) е сочувана преку FK TrainingSessions.training_user_id -> TRAINING_USERS(user_id).
- Assets(asset_id, user_id, asset_ticker_symbol, asset_buy_price, asset_buy_date, asset_quantity)
R2 = R1 - {asset_ticker_symbol, asset_buy_price, asset_buy_date, asset_quantity}
R2 = {user_id, email, username, password, training_gender, training_age, training_weight, training_id, asset_id, weight_current, weight_height, weight_goal_weight, weight_goal_calories, daily_intake_id, daily_intake_date, daily_intake_calories, discipline_user_id, custom_tracking_id, custom_tracking_name, task_id, task_name, task_is_finished, daily_completion_id, daily_completion_date, daily_completion_procent, finance_spending_budget, finance_saving_budget, finance_investing_budget, finance_donation_budget, finance_credit, income_id, income_date, income_amount}
Lossless join: Релацијата може да се реконструира преку join со asset_id.
Dependency preservation: FD7 е сочувана преку новата релација Assets.
- DailyIntakes(daily_intake_id, user_id, daily_intake_date, daily_intake_calories)
R3 = R2 - {daily_intake_date, daily_intake_calories}
R3 = {user_id, email, username, password, training_gender, training_age, training_weight, training_id, asset_id, daily_intake_id, weight_current, weight_height, weight_goal_weight, weight_goal_calories, discipline_user_id, custom_tracking_id, custom_tracking_name, task_id, task_name, task_is_finished, daily_completion_id, daily_completion_date, daily_completion_procent, finance_spending_budget, finance_saving_budget, finance_investing_budget, finance_donation_budget, finance_credit, income_id, income_date, income_amount}
Lossless join: Релацијата може да се реконструира преку join со daily_intake_id.
Dependency preservation: FD6 е сочувана преку новата релација DailyIntakes.
- Tasks(task_id, discipline_user_id, custom_tracking_id, task_name, task_is_finished)
R4 = R3 - {task_name, task_is_finished}
R4 = {user_id, email, username, password, training_gender, training_age, training_weight, training_id, asset_id, daily_intake_id, weight_current, weight_height, weight_goal_weight, weight_goal_calories, discipline_user_id, custom_tracking_id, custom_tracking_name, task_id, daily_completion_id, daily_completion_date, daily_completion_procent, finance_spending_budget, finance_saving_budget, finance_investing_budget, finance_donation_budget, finance_credit, income_id, income_date, income_amount}
Lossless join: Релацијата може да се реконструира преку join со task_id.
Dependency preservation: FD10 е сочувана преку новата релација Tasks.
- DailyCompletion(daily_completion_id, user_id, daily_completion_date, daily_completion_procent)
R5 = R4 - {daily_completion_date, daily_completion_procent}
R5 = {user_id, email, username, password, training_gender, training_age, training_weight, training_id, asset_id, daily_intake_id, weight_current, weight_height, weight_goal_weight, weight_goal_calories, discipline_user_id, custom_tracking_id, custom_tracking_name, task_id, daily_completion_id, finance_spending_budget, finance_saving_budget, finance_investing_budget, finance_donation_budget, finance_credit, income_id, income_date, income_amount}
Lossless join: Релацијата може да се реконструира преку join со daily_completion_id.
Dependency preservation: FD11 е сочувана преку новата релација DailyCompletion.
- Incomes(income_id, user_id, income_date, income_amount)
R6 = R5 - {income_date, income_amount}
R6 = {user_id, email, username, password, training_gender, training_age, training_weight, training_id, asset_id, daily_intake_id, weight_current, weight_height, weight_goal_weight, weight_goal_calories, discipline_user_id, custom_tracking_id, custom_tracking_name, task_id, daily_completion_id, income_id, finance_spending_budget, finance_saving_budget, finance_investing_budget, finance_donation_budget, finance_credit}
Lossless join: Релацијата може да се реконструира преку join со income_id.
Dependency preservation: FD14 е сочувана преку новата релација Incomes.
Добиени релации по декомпозицијата по 2НФ:
- TrainingSessions(training_id, training_user_id, training_date, training_type, training_duration, training_calories)
- Assets(asset_id, user_id, asset_ticker_symbol, asset_buy_price, asset_buy_date, asset_quantity)
- DailyIntakes(daily_intake_id, user_id, daily_intake_date, daily_intake_calories)
- Tasks(task_id, discipline_user_id, custom_tracking_id, task_name, task_is_finished)
- DailyCompletion(daily_completion_id, user_id, daily_completion_date, daily_completion_procent)
- Incomes(income_id, user_id, income_date, income_amount)
- R6 = {user_id, email, username, password, training_gender, training_age, training_weight, training_id, asset_id, daily_intake_id, weight_current, weight_height, weight_goal_weight, weight_goal_calories, discipline_user_id, custom_tracking_id, custom_tracking_name, task_id, daily_completion_id, income_id, finance_spending_budget, finance_saving_budget, finance_investing_budget, finance_donation_budget, finance_credit}
Проверка за 3НФ
3НФ бара: релацијата е во 2НФ и нема транзитивна зависност — не-клучен атрибут не смее да зависи од друг не-клучен атрибут.
Поради постоење на транзитивни зависности во R6, заклучуваме дека е нарушена 3НФ. На пример: user_id (не-клучен во R6) детерминира email, username, password, training_gender, weight_current, finance_spending_budget итн.
Декомпозиција по 3НФ
Сите транзитивни зависности се наоѓаат во R6. Ги разгледуваме чекор по чекор:
- Во R6 важи FD1:
user_id -> email, username, password.
Декомпозираме по FD1 и добиваме Users(user_id, email, username, password).
R7 = R6 - {email, username, password}
R7 = {user_id, training_gender, training_age, training_weight, training_id, asset_id, daily_intake_id, weight_current, weight_height, weight_goal_weight, weight_goal_calories, discipline_user_id, custom_tracking_id, custom_tracking_name, task_id, daily_completion_id, income_id, finance_spending_budget, finance_saving_budget, finance_investing_budget, finance_donation_budget, finance_credit}
Lossless join: Релацијата може да се реконструира преку join со user_id.
Dependency preservation: FD1 е сочувана преку Users.
- Во R7 важи FD2:
user_id -> training_gender, training_age, training_weight.
Декомпозираме по FD2 и добиваме TrainingUsers(user_id, training_gender, training_age, training_weight) — user_id е примарен и надворешен клуч кон Users.
R8 = R7 - {training_gender, training_age, training_weight}
R8 = {user_id, training_id, asset_id, daily_intake_id, weight_current, weight_height, weight_goal_weight, weight_goal_calories, discipline_user_id, custom_tracking_id, custom_tracking_name, task_id, daily_completion_id, income_id, finance_spending_budget, finance_saving_budget, finance_investing_budget, finance_donation_budget, finance_credit}
Lossless join: Релацијата може да се реконструира преку join со user_id.
Dependency preservation: FD2 е сочувана преку TrainingUsers.
- Во R8 важи FD5:
user_id -> weight_current, weight_height, weight_goal_weight, weight_goal_calories.
Декомпозираме по FD5 и добиваме WeightUsers(user_id, weight_current, weight_height, weight_goal_weight, weight_goal_calories) — user_id е примарен и надворешен клуч кон Users. DailyIntakes.user_id е FK кон WeightUsers.user_id.
R9 = R8 - {weight_current, weight_height, weight_goal_weight, weight_goal_calories}
R9 = {user_id, training_id, asset_id, daily_intake_id, discipline_user_id, custom_tracking_id, custom_tracking_name, task_id, daily_completion_id, income_id, finance_spending_budget, finance_saving_budget, finance_investing_budget, finance_donation_budget, finance_credit}
Lossless join: Релацијата може да се реконструира преку join со user_id.
Dependency preservation: FD5 е сочувана преку WeightUsers.
- Во R9 важи FD13:
user_id -> finance_spending_budget, finance_saving_budget, finance_investing_budget, finance_donation_budget, finance_credit.
Декомпозираме по FD13 и добиваме FinanceUsers(user_id, spending_budget, saving_budget, investing_budget, donation_budget, credit) — user_id е примарен и надворешен клуч кон Users. Incomes.user_id е FK кон FinanceUsers.user_id.
R10 = R9 - {finance_spending_budget, finance_saving_budget, finance_investing_budget, finance_donation_budget, finance_credit}
R10 = {user_id, training_id, asset_id, daily_intake_id, discipline_user_id, custom_tracking_id, custom_tracking_name, task_id, daily_completion_id, income_id}
Lossless join: Релацијата може да се реконструира преку join со user_id.
Dependency preservation: FD13 е сочувана преку FinanceUsers.
- Во R10 важи FD9:
custom_tracking_id -> user_id, custom_tracking_name.
Декомпозираме по FD9 и добиваме CustomTrackingCategories(custom_tracking_id, user_id, custom_tracking_name) — custom_tracking_id е примарен клуч, user_id е надворешен клуч кон Users.
R11 = R10 - {custom_tracking_name}
R11 = {user_id, training_id, asset_id, daily_intake_id, discipline_user_id, custom_tracking_id, task_id, daily_completion_id, income_id}
Lossless join: Релацијата може да се реконструира преку join со custom_tracking_id.
Dependency preservation: FD9 е сочувана преку CustomTrackingCategories.
- Во Tasks(task_id, discipline_user_id, custom_tracking_id, task_name, task_is_finished) важи FD8:
discipline_user_id -> user_id. Бидејќиdiscipline_user_idвоTasksсекогаш е еднакво наuser_idна корисникот, декомпозираме:
DisciplineUsers(user_id) — user_id е примарен и надворешен клуч кон Users. discipline_user_id во Tasks е FK кон DisciplineUsers.user_id.
Lossless join: Релацијата може да се реконструира преку join со discipline_user_id.
Dependency preservation: FD8 е сочувана преку FK врската discipline_user_id -> DisciplineUsers.user_id.
- InvestorUsers(user_id) е структурна (role) табела —
user_idе примарен и надворешен клуч конUsers.Assets.user_idе FK конInvestorUsers.user_id, со што се обезбедува дека само корисници со инвеститорски профил можат да поседуваат активи. Не произлегува од FD нарушување туку од дизајнерскиот избор за разграничување на профилите.
Lossless join: Релацијата може да се реконструира преку join со user_id.
Добиени релации по декомпозицијата по 3НФ:
- Users(user_id, email, username, password)
- TrainingUsers(user_id, training_gender, training_age, training_weight)
- TrainingSessions(training_id, training_user_id, training_date, training_type, training_duration, training_calories)
- WeightUsers(user_id, weight_current, weight_height, weight_goal_weight, weight_goal_calories)
- DailyIntakes(daily_intake_id, user_id, daily_intake_date, daily_intake_calories)
- InvestorUsers(user_id)
- Assets(asset_id, user_id, asset_ticker_symbol, asset_buy_price, asset_buy_date, asset_quantity)
- DisciplineUsers(user_id)
- CustomTrackingCategories(custom_tracking_id, user_id, custom_tracking_name)
- Tasks(task_id, discipline_user_id, custom_tracking_id, task_name, task_is_finished)
- DailyCompletion(daily_completion_id, user_id, daily_completion_date, daily_completion_procent)
- TaskDailyCompletion(task_id, daily_completion_id)
- FinanceUsers(user_id, spending_budget, saving_budget, investing_budget, donation_budget, credit)
- Incomes(income_id, user_id, income_date, income_amount)
- R11 = {user_id, training_id, asset_id, daily_intake_id,
discipline_user_id, custom_tracking_id, task_id, daily_completion_id, income_id}
Проверка дали добиените релации се во 3НФ
3НФ бара: за секоја нетривијална FD X → Y, X мора да биде суперклуч или Y мора да биде дел од некој кандидат клуч.
TRAINING_SESSIONS(training_id, training_user_id, training_date, training_type, training_duration, training_calories)
- PK:
training_id - Не-клучен
training_user_idне детерминира ништо во оваа релација → во 3НФ ✓
ASSETS(asset_id, user_id, asset_ticker_symbol, asset_buy_price, asset_buy_date, asset_quantity)
- PK:
asset_id - Не-клучен
user_idне детерминира ништо во оваа релација → во 3НФ ✓
DAILY_INTAKES(daily_intake_id, user_id, daily_intake_date, daily_intake_calories)
- PK:
daily_intake_id - Не-клучен
user_idне детерминира ништо во оваа релација → во 3НФ ✓
CUSTOM_TRACKING_CATEGORIES(custom_tracking_id, user_id, custom_tracking_name)
- PK:
custom_tracking_id - Не-клучен
user_idне детерминира ништо во оваа релација → во 3НФ ✓
TASKS(task_id, discipline_user_id, custom_tracking_id, task_name, task_is_finished)
- PK:
task_id - Ниту
discipline_user_idнитуcustom_tracking_idдетерминираат ништо во оваа релација → во 3НФ ✓ - (Условот CHECK (точно еден FK е NOT NULL) е ограничување на домен, не FD)
INCOMES(income_id, user_id, income_date, income_amount)
- PK:
income_id - Не-клучен
user_idне детерминира ништо во оваа релација → во 3НФ ✓
Заклучок: Сите релации се во 3НФ. Понатамошна декомпозиција не е потребна.
Проверка за БКНФ
БКНФ бара: за секоја нетривијална FD X -> Y, X мора да биде суперклуч.
Примарните клучеви се болдирани.
- Users(user_id, email, username, password) -> задоволува БКНФ
(
user_id,emailиusernameсе сите кандидат клучеви — сите детерминанти во оваа релација се суперклучеви.UNIQUEconstraints наemailиusernameги енфорцираат алтернативните кандидат клучеви.)
- TrainingUsers(user_id, training_gender, training_age, training_weight) -> задоволува БКНФ
- TrainingSessions(training_id, training_user_id, training_date, training_type, training_duration, training_calories) -> задоволува БКНФ
- WeightUsers(user_id, weight_current, weight_height, weight_goal_weight, weight_goal_calories) -> задоволува БКНФ
- DailyIntakes(daily_intake_id, user_id, daily_intake_date, daily_intake_calories) -> задоволува БКНФ
- InvestorUsers(user_id) -> задоволува БКНФ
- Assets(asset_id, user_id, asset_ticker_symbol, asset_buy_price, asset_buy_date, asset_quantity) -> задоволува БКНФ
- DisciplineUsers(user_id) -> задоволува БКНФ
- CustomTrackingCategories(custom_tracking_id, user_id, custom_tracking_name) -> задоволува БКНФ
- Tasks(task_id, discipline_user_id, custom_tracking_id, task_name, task_is_finished) -> задоволува БКНФ
- DailyCompletion(daily_completion_id, user_id, daily_completion_date, daily_completion_procent) -> задоволува БКНФ
- TaskDailyCompletion(task_id, daily_completion_id) -> задоволува БКНФ
- FinanceUsers(user_id, spending_budget, saving_budget, investing_budget, donation_budget, credit) -> задоволува БКНФ
- Incomes(income_id, user_id, income_date, income_amount) -> задоволува БКНФ
- R11 = {training_id, asset_id, user_id, daily_intake_id, discipline_user_id, custom_tracking_id, task_id, daily_completion_id, income_id} -> задоволува БКНФ
Заклучок: Целиот модел е во БКНФ.
Проверка за 4НФ
4НФ бара: релацијата е во БКНФ и нема нетривијални мултивредносни зависности — за секоја MVD X →→ Y, X мора да биде суперклуч.
Сите профилски релации (TrainingUsers, WeightUsers, InvestorUsers, DisciplineUsers, FinanceUsers) се во 1:1 врска со Users — секој корисник има точно еден профил од секој тип. Ова значи дека постои функционална, не мултивредносна зависност, и MVD не се применува.
R11 содржи само примарни и надворешни клучеви кои меѓусебно имаат 1:1 зависности — нема независни мултивредносни факти во истата релација.
TaskDailyCompletion(**task_id, daily_completion_id**) е спојна релација со композитен примарен клуч — нема не-клучни атрибути, па MVD не се применува.
Заклучок: Ниту една релација не содржи нетривијална мултивредносна зависност. Целиот модел е во 4НФ. Понатамошна декомпозиција не е потребна.
Финален резултат и дискусија
Нормализиран релациски модел
USERS(user_id, email, username, password) PK: user_id CK: {user_id}, {email}, {username}
TRAINING_USERS(user_id, gender, age, weight) PK: user_id FK: user_id -> USERS
TRAINING_SESSIONS(training_id, training_user_id, date, type, duration, calories) PK: training_id FK: training_user_id -> TRAINING_USERS(user_id)
WEIGHT_USERS(user_id, weight, height, goal_weight, goal_calories) PK: user_id FK: user_id -> USERS
DAILY_INTAKES(daily_intake_id, user_id, date, calories) PK: daily_intake_id FK: user_id -> WEIGHT_USERS(user_id)
INVESTOR_USERS(user_id) PK: user_id FK: user_id -> USERS
ASSETS(asset_id, user_id, ticker_symbol, buy_price, buy_date, quantity) PK: asset_id FK: user_id -> INVESTOR_USERS(user_id)
DISCIPLINE_USERS(user_id) PK: user_id FK: user_id -> USERS
CUSTOM_TRACKING_CATEGORIES(custom_tracking_id, user_id, name) PK: custom_tracking_id FK: user_id -> USERS
TASKS(task_id, discipline_user_id, custom_tracking_id, name, is_finished) PK: task_id FK: discipline_user_id -> DISCIPLINE_USERS(user_id) FK: custom_tracking_id -> CUSTOM_TRACKING_CATEGORIES CHECK: точно еден од двата FK е NOT NULL
DAILY_COMPLETION(daily_completion_id, user_id, date, procent) PK: daily_completion_id FK: user_id -> USERS
TASK_DAILY_COMPLETION(task_id, daily_completion_id) PK: {task_id, daily_completion_id} FK: task_id -> TASKS FK: daily_completion_id -> DAILY_COMPLETION
FINANCE_USERS(user_id, spending_budget, saving_budget, investing_budget, donation_budget, credit) PK: user_id FK: user_id -> USERS
INCOMES(income_id, user_id, date, amount) PK: income_id FK: user_id -> FINANCE_USERS(user_id)
Дискусија
Разлики во однос на претходниот дизајн (Phase 2):
- Отстранети
num_tasksиtasksодDISCIPLINE_USERSиCUSTOM_TRACKING_CATEGORIES.num_tasksе изведен атрибут (COUNTнаTASKS) — чување на изведени вредности го нарушува принципот на нормализација и создава ризик од инконзистентност.tasks(TEXTлиста) е не-атомски атрибут и директно ја нарушува1НФ.
- Профилските табели користат
user_idи како PK и како FK.TRAINING_USERS,WEIGHT_USERS,INVESTOR_USERS,DISCIPLINE_USERSиFINANCE_USERSго користатuser_idи како примарен и како надворешен клуч конUSERS. Ова е shared primary key pattern (1:1 субтип врска). Во глобалната релација R, атрибутитеtraining_user_idиdiscipline_user_idсе задржуваат бидејќи се реални FK колони воTRAINING_SESSIONSиTASKSсоодветно.
- Додадени
UNIQUEconstraints наemailиusernameвоUSERS. Нормализацијата формално идентификуваше{email}и{username}како кандидат клучеви, па DDL мора да ги енфорцира.
GENERATED BY DEFAULT AS IDENTITYза сите сурогатниPK-а. Технички подобрување усогласено со нормализираниот модел.
Модел за употреба во понатамошните фази: нормализираниот модел (финалниот DDL од Phase 5).
Заклучок
При нормализацијата ги добивме истите релации од претходниот дизајн, со исклучок на формалното отстранување на изведените и не-атомски атрибути (num_tasks, tasks). Профилските табели правилно го користат user_id и како примарен и надворешен клуч, со што финалниот нормализиран модел целосно се совпаѓа со DDL. Конечниот модел задоволува 1НФ, 2НФ, 3НФ и БКНФ, обезбедувајќи целосна декомпозиција без губење на податоци (lossless join) и без губење на функционални зависности (dependency preservation).
