wiki:Normalization

Version 13 (modified by 233062, 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_calories
  • asset_ticker_symbol, asset_buy_price, asset_buy_date, asset_quantity
  • weight_current, weight_height, weight_goal_weight, weight_goal_calories
  • daily_intake_date, daily_intake_calories
  • custom_tracking_name, task_name, task_is_finished
  • daily_completion_date, daily_completion_procent
  • finance_spending_budget, finance_saving_budget, finance_investing_budget, finance_donation_budget, finance_credit
  • income_date, income_amount
  • email, 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

Затворање на минималниот суперклуч

Покривачи:

  1. 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 -> Не ги содржи сите атрибути
  2. 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 -> Не ги содржи сите атрибути
  3. daily_intake_id+ -> {daily_intake_id, user_id, daily_intake_date, daily_intake_calories} via FD6 -> {email, username, password, ...} via FD1,FD2,FD5,FD13 -> Не ги содржи сите атрибути
  4. 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 -> Не ги содржи сите атрибути
  5. daily_completion_id+ -> {daily_completion_id, user_id, daily_completion_date, daily_completion_procent} via FD11 -> {email, ...} via FD1,FD2,FD5,FD13 -> Не ги содржи сите атрибути
  6. 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НФ

  1. 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).

  1. 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.

  1. 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.

  1. 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.

  1. 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.

  1. 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НФ:

  1. TrainingSessions(training_id, training_user_id, training_date, training_type, training_duration, training_calories)
  2. Assets(asset_id, user_id, asset_ticker_symbol, asset_buy_price, asset_buy_date, asset_quantity)
  3. DailyIntakes(daily_intake_id, user_id, daily_intake_date, daily_intake_calories)
  4. Tasks(task_id, discipline_user_id, custom_tracking_id, task_name, task_is_finished)
  5. DailyCompletion(daily_completion_id, user_id, daily_completion_date, daily_completion_procent)
  6. Incomes(income_id, user_id, income_date, income_amount)
  7. 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. Ги разгледуваме чекор по чекор:

  1. Во 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.

  1. Во 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.

  1. Во 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.

  1. Во 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.

  1. Во 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.

  1. Во 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.

  1. InvestorUsers(user_id) е структурна (role) табела — user_id е примарен и надворешен клуч кон Users. Assets.user_id е FK кон InvestorUsers.user_id, со што се обезбедува дека само корисници со инвеститорски профил можат да поседуваат активи. Не произлегува од FD нарушување туку од дизајнерскиот избор за разграничување на профилите.

Lossless join: Релацијата може да се реконструира преку join со user_id.

Добиени релации по декомпозицијата по 3НФ:

  1. Users(user_id, email, username, password)
  2. TrainingUsers(user_id, training_gender, training_age, training_weight)
  3. TrainingSessions(training_id, training_user_id, training_date, training_type, training_duration, training_calories)
  4. WeightUsers(user_id, weight_current, weight_height, weight_goal_weight, weight_goal_calories)
  5. DailyIntakes(daily_intake_id, user_id, daily_intake_date, daily_intake_calories)
  6. InvestorUsers(user_id)
  7. Assets(asset_id, user_id, asset_ticker_symbol, asset_buy_price, asset_buy_date, asset_quantity)
  8. DisciplineUsers(user_id)
  9. CustomTrackingCategories(custom_tracking_id, user_id, custom_tracking_name)
  10. Tasks(task_id, discipline_user_id, custom_tracking_id, task_name, task_is_finished)
  11. DailyCompletion(daily_completion_id, user_id, daily_completion_date, daily_completion_procent)
  12. TaskDailyCompletion(task_id, daily_completion_id)
  13. FinanceUsers(user_id, spending_budget, saving_budget, investing_budget, donation_budget, credit)
  14. Incomes(income_id, user_id, income_date, income_amount)
  15. 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 мора да биде суперклуч.

Примарните клучеви се болдирани.

  1. Users(user_id, email, username, password) -> задоволува БКНФ (user_id, email и username се сите кандидат клучеви — сите детерминанти во оваа релација се суперклучеви. UNIQUE constraints на email и username ги енфорцираат алтернативните кандидат клучеви.)
  1. TrainingUsers(user_id, training_gender, training_age, training_weight) -> задоволува БКНФ
  1. TrainingSessions(training_id, training_user_id, training_date, training_type, training_duration, training_calories) -> задоволува БКНФ
  1. WeightUsers(user_id, weight_current, weight_height, weight_goal_weight, weight_goal_calories) -> задоволува БКНФ
  1. DailyIntakes(daily_intake_id, user_id, daily_intake_date, daily_intake_calories) -> задоволува БКНФ
  1. InvestorUsers(user_id) -> задоволува БКНФ
  1. Assets(asset_id, user_id, asset_ticker_symbol, asset_buy_price, asset_buy_date, asset_quantity) -> задоволува БКНФ
  1. DisciplineUsers(user_id) -> задоволува БКНФ
  1. CustomTrackingCategories(custom_tracking_id, user_id, custom_tracking_name) -> задоволува БКНФ
  1. Tasks(task_id, discipline_user_id, custom_tracking_id, task_name, task_is_finished) -> задоволува БКНФ
  1. DailyCompletion(daily_completion_id, user_id, daily_completion_date, daily_completion_procent) -> задоволува БКНФ
  1. TaskDailyCompletion(task_id, daily_completion_id) -> задоволува БКНФ
  1. FinanceUsers(user_id, spending_budget, saving_budget, investing_budget, donation_budget, credit) -> задоволува БКНФ
  1. Incomes(income_id, user_id, income_date, income_amount) -> задоволува БКНФ
  1. 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):

  1. Отстранети num_tasks и tasks од DISCIPLINE_USERS и CUSTOM_TRACKING_CATEGORIES. num_tasks е изведен атрибут (COUNT на TASKS) — чување на изведени вредности го нарушува принципот на нормализација и создава ризик од инконзистентност. tasks (TEXT листа) е не-атомски атрибут и директно ја нарушува 1НФ.
  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 соодветно.
  1. Додадени UNIQUE constraints на email и username во USERS. Нормализацијата формално идентификуваше {email} и {username} како кандидат клучеви, па DDL мора да ги енфорцира.
  1. GENERATED BY DEFAULT AS IDENTITY за сите сурогатни PK-а. Технички подобрување усогласено со нормализираниот модел.

Модел за употреба во понатамошните фази: нормализираниот модел (финалниот DDL од Phase 5).

Заклучок

При нормализацијата ги добивме истите релации од претходниот дизајн, со исклучок на формалното отстранување на изведените и не-атомски атрибути (num_tasks, tasks). Профилските табели правилно го користат user_id и како примарен и надворешен клуч, со што финалниот нормализиран модел целосно се совпаѓа со DDL. Конечниот модел задоволува 1НФ, 2НФ, 3НФ и БКНФ, обезбедувајќи целосна декомпозиција без губење на податоци (lossless join) и без губење на функционални зависности (dependency preservation).

Note: See TracWiki for help on using the wiki.