| 1 | # **Нормализација**
|
|---|
| 2 |
|
|---|
| 3 | == **Де-нормализирана база на податоци**
|
|---|
| 4 |
|
|---|
| 5 | Се тргнува од една глобална, де-нормализирана релација што ги содржи атрибутите од целиот модел:
|
|---|
| 6 |
|
|---|
| 7 | `R = { user_id, email, username, password, training_user_id, training_gender, training_age, training_weight, training_id, training_date, training_type, training_duration, training_calories, investor_user_id, asset_id, asset_ticker_symbol, asset_buy_price, asset_buy_date, asset_quantity, weight_user_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, name, is_finished, daily_completion_id, daily_completion_date, procent, finance_user_id, finance_spending_budget, finance_saving_budget, finance_investing_budget, finance_donation_budget, finance_credit, income_id, income_date, income_amount }`
|
|---|
| 8 |
|
|---|
| 9 | Оваа релација е де-нормализирана затоа што содржи повторливи групи, како и зависности од атрибути што не се клуч.
|
|---|
| 10 |
|
|---|
| 11 | == **Функционални зависности**
|
|---|
| 12 |
|
|---|
| 13 | - **FD1:** `user_id -> email, username, password`
|
|---|
| 14 | - **FD2:** `email -> user_id, username, password`
|
|---|
| 15 | - **FD3:** `username -> user_id, email, password`
|
|---|
| 16 | - **FD4:** `training_user_id -> training_gender, training_age, training_weight`
|
|---|
| 17 | - **FD5:** `training_id -> training_user_id, training_date, training_type, training_duration, training_calories`
|
|---|
| 18 | - **FD6:** `investor_user_id -> user_id`
|
|---|
| 19 | - **FD7:** `asset_id -> investor_user_id, asset_ticker_symbol, asset_buy_price, asset_buy_date, asset_quantity`
|
|---|
| 20 | - **FD8:** `weight_user_id -> weight_current, weight_height, weight_goal_weight, weight_goal_calories`
|
|---|
| 21 | - **FD9:** `daily_intake_id -> weight_user_id, daily_intake_date, daily_intake_calories`
|
|---|
| 22 | - **FD10:** `discipline_user_id -> user_id`
|
|---|
| 23 | - **FD11:** `custom_tracking_id -> user_id, custom_tracking_name`
|
|---|
| 24 | - **FD12:** `task_id -> discipline_user_id, custom_tracking_id, name, is_finished`
|
|---|
| 25 | - **FD13:** `daily_completion_id -> user_id, daily_completion_date, procent`
|
|---|
| 26 | - **FD14:** `(task_id, daily_completion_id) -> /`
|
|---|
| 27 | - **FD15:** `finance_user_id -> finance_spending_budget, finance_saving_budget, finance_investing_budget, finance_donation_budget, finance_credit`
|
|---|
| 28 | - **FD16:** `income_id -> finance_user_id, income_date, income_amount`
|
|---|
| 29 |
|
|---|
| 30 | **Леви и десен дел:**
|
|---|
| 31 |
|
|---|
| 32 | - **Леви:** `user_id, email, username, training_user_id, training_id, investor_user_id, asset_id, weight_user_id, daily_intake_id, discipline_user_id, custom_tracking_id, task_id, daily_completion_id, finance_user_id, income_id`
|
|---|
| 33 | - **Десен:** `password, 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, name, is_finished, daily_completion_date, procent, finance_spending_budget, finance_saving_budget, finance_investing_budget, finance_donation_budget, finance_credit, income_date, income_amount`
|
|---|
| 34 |
|
|---|
| 35 | == **Глобална релација**
|
|---|
| 36 |
|
|---|
| 37 | `R = { user_id, email, username, password, training_user_id, training_gender, training_age, training_weight, training_id, training_date, training_type, training_duration, training_calories, investor_user_id, asset_id, asset_ticker_symbol, asset_buy_price, asset_buy_date, asset_quantity, weight_user_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, name, is_finished, daily_completion_id, daily_completion_date, procent, finance_user_id, finance_spending_budget, finance_saving_budget, finance_investing_budget, finance_donation_budget, finance_credit, income_id, income_date, income_amount }`
|
|---|
| 38 |
|
|---|
| 39 | == **Покривачи**
|
|---|
| 40 |
|
|---|
| 41 | - **`user_id+`** = { `user_id`, `email`, `username`, `password` } -> **не ги содржи сите атрибути**
|
|---|
| 42 | - **`training_user_id+`** = { `training_user_id`, `training_gender`, `training_age`, `training_weight` } -> **не ги содржи сите атрибути**
|
|---|
| 43 | - **`training_id+`** = { `training_id`, `training_user_id`, `training_date`, `training_type`, `training_duration`, `training_calories` } -> **не ги содржи сите атрибути**
|
|---|
| 44 | - **`investor_user_id+`** = { `investor_user_id`, `user_id` } -> **не ги содржи сите атрибути**
|
|---|
| 45 | - **`asset_id+`** = { `asset_id`, `investor_user_id`, `asset_ticker_symbol`, `asset_buy_price`, `asset_buy_date`, `asset_quantity` } -> **не ги содржи сите атрибути**
|
|---|
| 46 | - **`weight_user_id+`** = { `weight_user_id`, `weight_current`, `weight_height`, `weight_goal_weight`, `weight_goal_calories` } -> **не ги содржи сите атрибути**
|
|---|
| 47 | - **`daily_intake_id+`** = { `daily_intake_id`, `weight_user_id`, `daily_intake_date`, `daily_intake_calories` } -> **не ги содржи сите атрибути**
|
|---|
| 48 | - **`discipline_user_id+`** = { `discipline_user_id`, `user_id` } -> **не ги содржи сите атрибути**
|
|---|
| 49 | - **`custom_tracking_id+`** = { `custom_tracking_id`, `user_id`, `custom_tracking_name` } -> **не ги содржи сите атрибути**
|
|---|
| 50 | - **`task_id+`** = { `task_id`, `discipline_user_id`, `custom_tracking_id`, `name`, `is_finished` } -> **не ги содржи сите атрибути**
|
|---|
| 51 | - **`daily_completion_id+`** = { `daily_completion_id`, `user_id`, `daily_completion_date`, `procent` } -> **не ги содржи сите атрибути**
|
|---|
| 52 | - **`finance_user_id+`** = { `finance_user_id`, `finance_spending_budget`, `finance_saving_budget`, `finance_investing_budget`, `finance_donation_budget`, `finance_credit` } -> **не ги содржи сите атрибути**
|
|---|
| 53 | - **`income_id+`** = { `income_id`, `finance_user_id`, `income_date`, `income_amount` } -> **не ги содржи сите атрибути**
|
|---|
| 54 | - **`{task_id, daily_completion_id}+`** = { `task_id`, `daily_completion_id` } -> **не ги содржи сите атрибути**
|
|---|
| 55 |
|
|---|
| 56 | == **Спојување покривачи**
|
|---|
| 57 |
|
|---|
| 58 | Бидејќи атрибутите што се појавуваат само на левата страна не можат да се изведат на друг начин, тие мора да бидат дел од примарниот клуч.
|
|---|
| 59 |
|
|---|
| 60 | Земајќи ги сите леви атрибути заедно, добиваме минимален суперклуч:
|
|---|
| 61 |
|
|---|
| 62 | `{ user_id, training_user_id, training_id, investor_user_id, asset_id, weight_user_id, daily_intake_id, discipline_user_id, custom_tracking_id, task_id, daily_completion_id, finance_user_id, income_id }`
|
|---|
| 63 |
|
|---|
| 64 | Во нашата нормализирана конструкција, ова е теоретскиот глобален кандидат-клуч на де-нормализираната релација.
|
|---|
| 65 |
|
|---|
| 66 | Избран примарен клуч: **`{ user_id, training_user_id, training_id, investor_user_id, asset_id, weight_user_id, daily_intake_id, discipline_user_id, custom_tracking_id, task_id, daily_completion_id, finance_user_id, income_id }`**
|
|---|
| 67 |
|
|---|
| 68 | == **Проверка за 1НФ**
|
|---|
| 69 |
|
|---|
| 70 | Бидејќи релацијата содржи повторливи групи и неатомски листи во де-нормализираниот поглед, **не ја задоволува 1НФ**.
|
|---|
| 71 |
|
|---|
| 72 | == **Декомпозиција по 1НФ**
|
|---|
| 73 |
|
|---|
| 74 | **Релација што се анализира**
|
|---|
| 75 |
|
|---|
| 76 | `R`
|
|---|
| 77 |
|
|---|
| 78 | **Проблем**
|
|---|
| 79 |
|
|---|
| 80 | Лист атрибутите и повторливите групи не се атомски.
|
|---|
| 81 |
|
|---|
| 82 | **Прва декомпозиција**
|
|---|
| 83 |
|
|---|
| 84 | - `TASKS(task_id, discipline_user_id, custom_tracking_id, name, is_finished)`
|
|---|
| 85 | - `TASK_DAILY_COMPLETION(task_id, daily_completion_id)`
|
|---|
| 86 |
|
|---|
| 87 | **Резултат**
|
|---|
| 88 |
|
|---|
| 89 | Се добиваат атомски вредности и релации со локални клучеви.
|
|---|
| 90 |
|
|---|
| 91 | == **Проверка за 2НФ**
|
|---|
| 92 |
|
|---|
| 93 | `R` **не ја задоволува 2НФ** поради парцијални зависности, на пример:
|
|---|
| 94 |
|
|---|
| 95 | - `user_id -> email, username, password`
|
|---|
| 96 | - `training_user_id -> training_gender, training_age, training_weight`
|
|---|
| 97 | - `asset_id -> asset_ticker_symbol, asset_buy_price, asset_buy_date, asset_quantity`
|
|---|
| 98 |
|
|---|
| 99 | == **Декомпозиција по 2НФ**
|
|---|
| 100 |
|
|---|
| 101 | Атрибутите се групираат според тоа од кој клуч зависат:
|
|---|
| 102 |
|
|---|
| 103 | - **`USERS(user_id, email, username, password)`**
|
|---|
| 104 | - **`TRAINING_USERS(user_id, gender, age, weight)`**
|
|---|
| 105 | - **`TRAINING_SESSIONS(training_id, training_user_id, date, type, duration, calories)`**
|
|---|
| 106 | - **`INVESTOR_USERS(user_id)`**
|
|---|
| 107 | - **`ASSETS(asset_id, user_id, ticker_symbol, buy_price, buy_date, quantity)`**
|
|---|
| 108 | - **`WEIGHT_USERS(user_id, weight, height, goal_weight, goal_calories)`**
|
|---|
| 109 | - **`DAILY_INTAKES(daily_intake_id, user_id, date, calories)`**
|
|---|
| 110 | - **`DISCIPLINE_USERS(user_id)`**
|
|---|
| 111 | - **`CUSTOM_TRACKING_CATEGORIES(custom_tracking_id, user_id, name)`**
|
|---|
| 112 | - **`TASKS(task_id, discipline_user_id, custom_tracking_id, name, is_finished)`**
|
|---|
| 113 | - **`DAILY_COMPLETION(daily_completion_id, user_id, date, procent)`**
|
|---|
| 114 | - **`TASK_DAILY_COMPLETION(task_id, daily_completion_id)`**
|
|---|
| 115 | - **`FINANCE_USERS(user_id, spending_budget, saving_budget, investing_budget, donation_budget, credit)`**
|
|---|
| 116 | - **`INCOMES(income_id, user_id, date, amount)`**
|
|---|
| 117 |
|
|---|
| 118 | == **Проверка за 3НФ**
|
|---|
| 119 |
|
|---|
| 120 | Ги разгледуваме релациите добиени по 2НФ.
|
|---|
| 121 |
|
|---|
| 122 | Проблематични транзитивни/изведени атрибути се:
|
|---|
| 123 |
|
|---|
| 124 | - **`num_tasks`**
|
|---|
| 125 | - **`tasks`**
|
|---|
| 126 | - **`weight_user_id`** во `TRAINING_SESSIONS` како непотребна зависност за оваа верзија на моделот
|
|---|
| 127 |
|
|---|
| 128 | == **Декомпозиција по 3НФ**
|
|---|
| 129 |
|
|---|
| 130 | 1. **`DISCIPLINE_USERS(user_id)`** -> се задржува само идентификаторот што ја врзува дисциплинската улога со `USERS`.
|
|---|
| 131 |
|
|---|
| 132 | 2. **`CUSTOM_TRACKING_CATEGORIES(custom_tracking_id, user_id, name)`** -> се задржува само името на категоријата, без броење и листи на задачи.
|
|---|
| 133 |
|
|---|
| 134 | 3. **`TRAINING_SESSIONS(training_id, training_user_id, date, type, duration, calories)`** -> сесијата се врзува само со `training_user_id`.
|
|---|
| 135 |
|
|---|
| 136 | **Резултат**
|
|---|
| 137 |
|
|---|
| 138 | Нема не-клучен атрибут што зависи од друг не-клучен атрибут во истата релација.
|
|---|
| 139 |
|
|---|
| 140 | == **Проверка за БКНФ**
|
|---|
| 141 |
|
|---|
| 142 | Сите добиени релации се во БКНФ, бидејќи секој детерминант е кандидат-клуч во својата релација.
|
|---|
| 143 |
|
|---|
| 144 | Особено:
|
|---|
| 145 |
|
|---|
| 146 | - **`USERS`** ги задржува алтернативните клучеви `email` и `username`
|
|---|
| 147 | - **`TASK_DAILY_COMPLETION(task_id, daily_completion_id)`** е спојна релација без не-клучни атрибути
|
|---|
| 148 | - профилните релации со `user_id` се чисти 1:1 продолжувања на `USERS`
|
|---|
| 149 |
|
|---|
| 150 | == **Финален резултат и дискусија**
|
|---|
| 151 |
|
|---|
| 152 | == **Нормализиран релациски модел**
|
|---|
| 153 |
|
|---|
| 154 | Финалниот модел што го користиме во тековната имплементација е:
|
|---|
| 155 |
|
|---|
| 156 | - **`USERS`**
|
|---|
| 157 | - **`TRAINING_USERS`**
|
|---|
| 158 | - **`TRAINING_SESSIONS`**
|
|---|
| 159 | - **`INVESTOR_USERS`**
|
|---|
| 160 | - **`ASSETS`**
|
|---|
| 161 | - **`WEIGHT_USERS`**
|
|---|
| 162 | - **`DAILY_INTAKES`**
|
|---|
| 163 | - **`DISCIPLINE_USERS`**
|
|---|
| 164 | - **`CUSTOM_TRACKING_CATEGORIES`**
|
|---|
| 165 | - **`TASKS`**
|
|---|
| 166 | - **`DAILY_COMPLETION`**
|
|---|
| 167 | - **`TASK_DAILY_COMPLETION`**
|
|---|
| 168 | - **`FINANCE_USERS`**
|
|---|
| 169 | - **`INCOMES`**
|
|---|
| 170 |
|
|---|
| 171 | == **Дискусија**
|
|---|
| 172 |
|
|---|
| 173 | Разлики во однос на претходниот дизајн:
|
|---|
| 174 |
|
|---|
| 175 | - Се отстранети **`num_tasks`** и **`tasks`** од дисциплинските табели.
|
|---|
| 176 | - Се отстранета непотребната релација **`weight_user_id`** од `TRAINING_SESSIONS`.
|
|---|
| 177 | - Се задржуваат профилните табели со **`user_id`** како заеднички примарен и странски клуч.
|
|---|
| 178 |
|
|---|
| 179 | Одлука за следните фази:
|
|---|
| 180 |
|
|---|
| 181 | - **Овој нормализиран модел** останува извор на вистина.
|
|---|
| 182 | - **API форматот** може да остане стабилен, додека внатрешната шема е нормализирана.
|
|---|
| 183 | - **DDL и DML скриптите** треба да ја следат оваа шема.
|
|---|