wiki:Normalization

Version 22 (modified by 201165, 4 days ago) ( diff )

--

Nutritioneer | Нормализација и оптимизација на дизајн

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

Рамна табела од сите ентитети на Nutritioneer и нивните врски, без структурни ограничувања. Повеќевредносните факти се присутни како групи што се повторуваат: еден рецепт има многу состојки, многу диететски ограничувања и многу коментари, а една состојка носи многу нутриенти.

Функционални зависности

  • user_id → email, username, password, role
  • email → user_id (алтернативен клуч, UNIQUE)
  • username → user_id (алтернативен клуч, UNIQUE)
  • ingredient_id → ingredient_name, ingr_description, energy, kcal, ingr_type
  • ingredient_name → ingredient_id (алтернативен клуч, UNIQUE)
  • nutrient_id → nutr_description, rdi_quantity, unit, nutr_type
  • nutr_description → nutrient_id (алтернативен клуч, UNIQUE)
  • restriction_id → restr_description, restr_type
  • restr_description → restriction_id (алтернативен клуч, UNIQUE)
  • recipe_id → recipe_name, guide, kcal_sum, servings, user_id
  • post_id → media, media_type, media_name, is_private, is_favourite, status, post_created_at, user_id, recipe_id
  • recipe_id → post_id (POSTS_ON е 1:1, UNIQUE врз recipe_id)
  • comment_id → comment_text, comment_created_at, post_id, user_id
  • list_id → list_date_time, list_notes, is_bought, list_kcal, user_id
  • planner_id → planner_date_time, planner_notes, planner_kcal, is_consumed, user_id
  • bio_id → bio_date, weight, height, age, muscle_fat_ratio, user_id
  • {user_id, bio_date} → bio_id (едно мерење по корисник на ден)
  • {recipe_id, ingredient_id} → ingredient_quantity
  • {ingredient_id, nutrient_id} → nutrient_amount
  • {list_id, ingredient_id} → buy_quantity

Чисти M:N врски

Врските како recipe - restriction и grocery_list - grocery_list_bulk_add_ingredient неможат да определат дополнителен атрибут. Не се функционални зависности.

Почетна денормализирана релација

R( user_id, email, username, password, role, ingredient_id, ingredient_name, ingr_description, energy, kcal, ingr_type, nutrient_id, nutr_description, rdi_quantity, unit, nutr_type, restriction_id, restr_description, restr_type, recipe_id, recipe_name, guide, kcal_sum, servings, post_id, media, media_type, media_name, is_private, is_favourite, status, post_created_at, comment_id, comment_text, comment_created_at, list_id, list_date_time, list_notes, is_bought, list_kcal, planner_id, planner_date_time, planner_notes, planner_kcal, is_consumed, bio_id, bio_date, weight, height, age, muscle_fat_ratio, ingredient_quantity, nutrient_amount, buy_quantity )

Кандидат-клуч

X е кандидат-клуч кога X+ = R (ги изведува сите атрибути) и ниедно вистинско подмножество на X го нема тоа својство -> минималност.

  1. X+ = R
  2. X е минимално

K+ се пресметува на следен начин:

K = { comment_id, ingredient_id, nutrient_id, restriction_id, list_id, planner_id, bio_id }
Чекор 0 - K+ = K:  comment_id, ingredient_id, nutrient_id, restriction_id, list_id, planner_id, bio_id

Чекор 1 - секој идентификатор ја активира сопствената зависност: 
comment_id → comment_text, comment_created_at, post_id, user_id;
ingredient_id → ingredient_name, ingr_description, energy, kcal, ingr_type;
nutrient_id → nutr_description, rdi_quantity, unit, nutr_type;
restriction_id → restr_description, restr_type;
list_id → list_date_time, list_notes, is_bought, list_kcal;
planner_id → planner_date_time, planner_notes, planner_kcal, is_consumed;
bio_id → bio_date, weight, height, age, muscle_fat_ratio;
{ingredient_id, nutrient_id} → nutrient_amount;
{list_id, ingredient_id} → buy_quantity

Чекор 2 - post_id и user_id влегоа во K+ во чекор 1, па сега се активираат:
post_id → media, media_type, media_name, is_private, is_favourite, status, post_created_at, recipe_id;
user_id → email, username, password, role

Чекор 3 - recipe_id влезе во K+ во чекор 2:
recipe_id → recipe_name, guide, kcal_sum, servings;
{recipe_id, ingredient_id} → ingredient_quantity

Чекор 4 - цело поминување низ зависностите не додава ништо -> К+ = R

Од тоа следи таблата од сите кандидати клучеви:

K# ingredient nutrient restriction biometrics
K1 ingredient_id nutrient_id restriction_id bio_id
K2 ingredient_id nutrient_id restriction_id bio_date
K3 ingredient_id nutrient_id restr_description bio_id
K4 ingredient_id nutrient_id restr_description bio_date
K5 ingredient_id nutr_description restriction_id bio_id
K6 ingredient_id nutr_description restriction_id bio_date
K7 ingredient_id nutr_description restr_description bio_id
K8 ingredient_id nutr_description restr_description bio_date
K9 ingredient_name nutrient_id restriction_id bio_id
K10 ingredient_name nutrient_id restriction_id bio_date
K11 ingredient_name nutrient_id restr_description bio_id
K12 ingredient_name nutrient_id restr_description bio_date
K13 ingredient_name nutr_description restriction_id bio_id
K14 ingredient_name nutr_description restriction_id bio_date
K15 ingredient_name nutr_description restr_description bio_id
K16 ingredient_name nutr_description restr_description bio_date

Секој од шеснаесетте содржи и {comment_id, list_id, planner_id}. K1 е целосно сурогат формата и токму таа е избрана за примарен клуч. Сурогат (идентификаторите) атрибутите се непроменливи, додека корисник може да преименува состојка и со тоа K16 да добие друга вредност за истиот ред.

Атрибут што не се појавува на десната страна на ниедна функционална зависност никогаш не може да биде изведен, па мора да припаѓа на секој клуч. Пресметка на затворањето на K:

  • comment_id → post_id, user_id; post_id → recipe_id; recipe_id → атрибути на рецептот; user_id → атрибути на корисникот
  • ingredient_id → атрибути на состојката; {recipe_id, ingredient_id} → ingredient_quantity
  • nutrient_id → атрибути на нутриентот; {ingredient_id, nutrient_id} → nutrient_amount
  • restriction_id, list_id, planner_id, bio_id → нивните гранки; {list_id, ingredient_id} → buy_quantity

Проверка на минималност

  1. Отстранување на comment_id -> Се губи крајот на синџирот коментар → објава → рецепт → корисник.
  2. Отстранување на ingredient_id -> Се губи каталогот на храна, а со него и сите три атрибути за количина на врските.
  3. Oтстранување на nutrient_id -> Се губи гранката на нутриенти и nutrient_amount.
  4. Oтстранување на restriction_id -> Се губи гранката на диететски ознаки
  5. Oтстранување на list_id, planner_id или bio_id -> Се губат соодветните ентитети. Ниедна од нив не е определена од ништо друго.

Примерок од денормализирани податоци

Истиот корисник, рецепт, состојка и количина на нутриент се повторуваат по еднаш за секој коментар и по еднаш за секое ограничување. Промена на име на рецепт би барала ажурирање на секој од овие редови — аномалија при промена — а бришењето на последниот коментар би го избришало и фактот дека рецептот содржи бел грав — аномалија при бришење.

username recipe name ingredient name nutrient descr nutrient amount restriction type comment
marija_t Tavce Gravce Dry White Beans Protein 21.0 vegan How long should I let it simmer and soak?
marija_t Tavce Gravce Dry White Beans Protein 21.0 vegan How long should you keep stirring the pot?
marija_t Tavce Gravce Dry White Beans Protein 21.0 gluten_free What is in the sauce?

ПРВА(1NF)

Релацијата е во 1NF кога:
• Редоследот на редовите нема значење
• Секој атрибут содржи атомски вредности
• Типовите на податоци се доследни
• Примарен клуч еднозначно го идентификува секој запис
• Не постојат групи што се повторуваат

Постапката за подготовка на рецептот се чува како една текстуална вредност наместо како листа чекори, бидејќи чекорите никогаш не се пребаруваат поединечно. Релацијата ја задоволува 1NF така што секој атрибут содржи една атомска вредност идентификувано со K.

ВТОРА(2NF)

За да се постигне 2NF:
• 1NF +
• Парцијалните зависности мора да се отстранат.
• Атрибутите што зависат само од дел од K мора да се издвојат во независни релации.
  • R -> Целосна шема
  • R′ -> Шема после издвојување

Секој атрибут на R зависи само од една компонента на K (или од пар извлечен од неа), никогаш од целиот K, па секоја гранка е парцијална зависност и се издвојува во сопствена релација.

Декомпозиција во 2NF

R1 - USER

Проверка на кандидат-клучот: user_id е примарен клуч. email и username се алтернативни (unique) K. Не постојат парцијални зависности.

{user_id} → {email, username, password, role}

Одделена релација:

  • R1 ( user_id, email, username, password, role )

Остаточна релација:

R′1 ( user_id, ingredient_id, ingredient_name, ingr_description, energy, kcal, ingr_type, nutrient_id, nutr_description, 
rdi_quantity, unit, nutr_type, restriction_id, restr_description, restr_type, recipe_id, recipe_name, guide, kcal_sum, servings, 
post_id, media, media_type, media_name, is_private, is_favourite, status, post_created_at, comment_id, comment_text, 
comment_created_at, list_id, list_date_time, list_notes, is_bought, list_kcal, planner_id, planner_date_time, planner_notes, 
planner_kcal, is_consumed, bio_id, bio_date, weight, height, age, muscle_fat_ratio, ingredient_quantity, nutrient_amount, 
buy_quantity )
  • lossless тест: R1 ∩ R′1 = {user_id}; {user_id} → R1
user_id email username password role
6 acko@… acko_l testPass@1 admin
8 marija.trajko@… marija_t testPass@1 user

R2 - INGREDIENT

Проверка на кандидат-клучот: ingredient_id е примарен клуч; ingredient_name е алтернативен К.

{ingredient_id} → {ingredient_name, ingr_description, energy, kcal, ingr_type}

Одделена релација:

  • R2 ( ingredient_id, ingredient_name, ingr_description, energy, kcal, ingr_type )

Остаточна релација:

R′2 ( user_id, ingredient_id, nutrient_id, nutr_description, rdi_quantity, unit, nutr_type, restriction_id, restr_description, 
restr_type, recipe_id, recipe_name, guide, kcal_sum, servings, post_id, media, media_type, media_name, is_private, is_favourite, 
status, post_created_at, comment_id, comment_text, comment_created_at, list_id, list_date_time, list_notes, is_bought, list_kcal, 
planner_id, planner_date_time, planner_notes, planner_kcal, is_consumed, bio_id, bio_date, weight, height, age, muscle_fat_ratio, 
ingredient_quantity, nutrient_amount, buy_quantity )
  • lossless тест: R2 ∩ R′2 = {ingredient_id}; {ingredient_id} → R2
ingredient_id name description energy kcal type
26 Onion White or Red, will make you cry 167 40 vegetable
28 Bread Plain Bread 1000 40 carbs

R3 - NUTRIENT

Проверка на кандидат-клучот: nutrient_id е примарен клуч; nutr_description е алтернативен K.

{nutrient_id} → {nutr_description, rdi_quantity, unit, nutr_type}

Одделена релација:

  • R3 ( nutrient_id, nutr_description, rdi_quantity, unit, nutr_type )

Остаточна релација:

R′3 ( user_id, ingredient_id, nutrient_id, restriction_id, restr_description, restr_type, recipe_id, recipe_name, guide, 
kcal_sum, servings, post_id, media, media_type, media_name, is_private, is_favourite, status, post_created_at, comment_id, 
comment_text, comment_created_at, list_id, list_date_time, list_notes, is_bought, list_kcal, planner_id, planner_date_time, 
planner_notes, planner_kcal, is_consumed, bio_id, bio_date, weight, height, age, muscle_fat_ratio, ingredient_quantity, 
nutrient_amount, buy_quantity )
  • lossless тест: R3 ∩ R′3 = {nutrient_id}; {nutrient_id} → R3
nutrient_id description quantity unit type
1 Protein 50 gram macro
9 Vitamin C 80 milligram micro

R4 - RESTRICTION

Проверка на кандидат-клучот: restriction_id е примарен клуч; restr_description е алтернативен K.

{restriction_id} → {restr_description, restr_type}

Одделена релација:

  • R4 ( restriction_id, restr_description, restr_type )

Остаточна релација:

R′4 ( user_id, ingredient_id, nutrient_id, restriction_id, recipe_id, recipe_name, guide, kcal_sum, servings, post_id, media, 
media_type, media_name, is_private, is_favourite, status, post_created_at, comment_id, comment_text, comment_created_at, list_id, 
list_date_time, list_notes, is_bought, list_kcal, planner_id, planner_date_time, planner_notes, planner_kcal, is_consumed, 
bio_id, bio_date, weight, height, age, muscle_fat_ratio, ingredient_quantity, nutrient_amount, buy_quantity )
  • lossless тест: R4 ∩ R′4 = {restriction_id}, а {restriction_id} → R4
restriction_id restr_description type
12 No animal origins vegan
20 Only animal origins carnivore

R5 - RECIPE

Проверка на кандидат-клучот: recipe_id е примарен клуч. user_id е надворешен клуч кон R1.

{recipe_id} → {recipe_name, guide, kcal_sum, servings, user_id}

Одделена релација:

  • R5 ( recipe_id, recipe_name, guide, kcal_sum, servings, user_id )

Остаточна релација:

R′5 ( user_id, ingredient_id, nutrient_id, restriction_id, recipe_id, post_id, media, media_type, media_name, is_private, 
is_favourite, status, post_created_at, comment_id, comment_text, comment_created_at, list_id, list_date_time, list_notes, 
is_bought, list_kcal, planner_id, planner_date_time, planner_notes, planner_kcal, is_consumed, bio_id, bio_date, weight, height, 
age, muscle_fat_ratio, ingredient_quantity, nutrient_amount, buy_quantity )
  • lossless тест: R5 ∩ R′5 = {recipe_id, user_id}, а {recipe_id} → R5.

Заедничкото множество е поширако од клучот затоа што надворешните клучеви на R5 остануваат во R′ за надмножество на детерминанта и понатаму е детерминанта.

recipe_id name guide kcal_sum servings user_id
1 Omlette 1.Get Eggs 2... 1167 1 2
2 Cesar Salad 1. Get Bowl 2... 2400 2 3

R6 - POST

Проверка на кандидат-клучот: post_id е примарен клуч. recipe_id е алтернативен клуч (UNIQUE, дозволува NULL), кој ја реализира врската POSTS_ON 1:1. user_id ја реализира CREATES.

{post_id} → {media, media_type, media_name, is_private, is_favourite, status, post_created_at, user_id, recipe_id}

Одделена релација:

  • R6 ( post_id, media, media_type, media_name, is_private, is_favourite, status, post_created_at, user_id, recipe_id )

Остаточна релација:

R′6 ( user_id, ingredient_id, nutrient_id, restriction_id, recipe_id, post_id, comment_id, comment_text, comment_created_at, 
list_id, list_date_time, list_notes, is_bought, list_kcal, planner_id, planner_date_time, planner_notes, planner_kcal, 
is_consumed, bio_id, bio_date, weight, height, age, muscle_fat_ratio, ingredient_quantity, nutrient_amount, buy_quantity )
  • lossless тест: R6 ∩ R′6 = {post_id, user_id, recipe_id}, а {post_id} → R6

Заедничкото множество е поширако од клучот затоа што надворешните клучеви на R6 остануваат во R′ за надмножество на детерминанта и понатаму е детерминанта.

post_id is_private is_favourite status created_at user_id recipe_id
1 true true published 2026-01-30 07:30 2 NULL
2 true false published 2026-03-20 09:35 3 1

R7 - COMMENT

Проверка на кандидат-клучот: comment_id е примарен клуч.

{comment_id} → {comment_text, comment_created_at, post_id, user_id}

Одделена релација:

  • R7 ( comment_id, comment_text, comment_created_at, post_id, user_id )

Остаточна релација:

R′7 ( user_id, ingredient_id, nutrient_id, restriction_id, recipe_id, post_id, comment_id, list_id, list_date_time, list_notes, 
is_bought, list_kcal, planner_id, planner_date_time, planner_notes, planner_kcal, is_consumed, bio_id, bio_date, weight, height, 
age, muscle_fat_ratio, ingredient_quantity, nutrient_amount, buy_quantity )
  • lossless тест: R7 ∩ R′7 = {comment_id, post_id, user_id}, а {comment_id} → R7

Заедничкото множество е поширако од клучот затоа што надворешните клучеви на R7 остануваат во R′ за надмножество на детерминанта и понатаму е детерминанта.

comment_id description created_at post_id user_id
12 Pozdrav do Baba Rada 2026-01-30 07:30 3 1
29 Vkusno i zdravo 2026-12-10 08:30 2 5

R8 - GROCERY_LIST

Проверка на кандидат-клучот: list_id е примарен клуч.

{list_id} → {list_date_time, list_notes, is_bought, list_kcal, user_id}

Одделена релација:

  • R8 ( list_id, list_date_time, list_notes, is_bought, list_kcal, user_id )

Одделена релација:

R′8 ( user_id, ingredient_id, nutrient_id, restriction_id, recipe_id, post_id, comment_id, list_id, planner_id, 
planner_date_time, planner_notes, planner_kcal, is_consumed, bio_id, bio_date, weight, height, age, muscle_fat_ratio, 
ingredient_quantity, nutrient_amount, buy_quantity )
  • lossless тест: R8 ∩ R′8 = {list_id, user_id}, а {list_id} → R8.

Заедничкото множество е поширако од клучот затоа што надворешните клучеви на R8 остануваат во R′ за надмножество на детерминанта и понатаму е детерминанта.

list_id date_time notes is_bought kcal user_id
12 2026-01-30 07:30 Sabota Rucek true 1167 2
23 2026-12-10 08:30 Igor Rodenden true 2400 3

R9 - INTAKE_PLANNER

Проверка на кандидат-клучот: planner_id е примарен клуч.

{planner_id} → {planner_date_time, planner_notes, planner_kcal, is_consumed, user_id}

Одделена релација:

  • R9 ( planner_id, planner_date_time, planner_notes, planner_kcal, is_consumed, user_id )

Остаточна релација:

R′9 ( user_id, ingredient_id, nutrient_id, restriction_id, recipe_id, post_id, comment_id, 
list_id, planner_id, bio_id, bio_date, 
weight, height, age, muscle_fat_ratio, ingredient_quantity, nutrient_amount, buy_quantity )
  • lossless тест: R9 ∩ R′9 = {planner_id, user_id}, а {planner_id} → R9

Заедничкото множество е поширако од клучот затоа што надворешните клучеви на R9 остануваат во R′ за надмножество на детерминанта и понатаму е детерминанта.

planner_id date_time notes kcal is_consumed user_id
12 2026-01-30 07:30 Sabota Rucek 1167 true 2
23 2026-12-10 08:30 Igor Rodenden 2400 true 3

R10 - BIOMETRICS

Проверка на кандидат-клучот: bio_id е примарен клуч; {user_id, bio_date} е алтернативен K.

{bio_id} → {bio_date, weight, height, age, muscle_fat_ratio, user_id}

Одделена релација:

  • R10 ( bio_id, bio_date, weight, height, age, muscle_fat_ratio, user_id )

Остаточна релација:

R′10 ( ingredient_id, nutrient_id, restriction_id, recipe_id, post_id, comment_id, list_id, planner_id, bio_id, 
ingredient_quantity, nutrient_amount, buy_quantity )
  • lossless тест: R10 ∩ R′10 = {bio_id}, а {bio_id} → R10
bio_id date weight height age muscle_fat_ratio user_id
1 2026-01-10 100 189 25 1
2 2026-01-11 99.5 189 25 1

R11 - RECIPE_CONTAINS_INGREDIENT

Проверка на кандидат-клучот: Составен примарен клуч {recipe_id, ingredient_id}. Количина зависи од целиот пар — ни самиот рецепт, ни самата состојка не ја определуваат.

{recipe_id, ingredient_id} → {ingredient_quantity}

Одделена релација:

  • R11 ( recipe_id, ingredient_id, ingredient_quantity )

Одделена релација:

R′11 ( ingredient_id, nutrient_id, restriction_id, recipe_id, post_id, comment_id, list_id,
planner_id, bio_id, nutrient_amount, buy_quantity )
  • lossless тест: R11 ∩ R′11 = {recipe_id, ingredient_id}, а {recipe_id, ingredient_id} → R11
recipe_id ingredient_id ingredient_quantity
1 20 100
2 30 450

R12 - INGREDIENT_CONTAINS_NUTRIENT

Проверка на кандидат-клучот: Составен примарен клуч {ingredient_id, nutrient_id}. Количината на 100 g зависи од целиот пар.

{ingredient_id, nutrient_id} → {nutrient_amount}

Одделена релација:

  • R12 ( ingredient_id, nutrient_id, nutrient_amount )

Остаточна релација:

R′12 ( ingredient_id, nutrient_id, restriction_id, recipe_id,
post_id, comment_id, list_id, planner_id, bio_id, buy_quantity )
  • lossless тест: R12 ∩ R′12 = {ingredient_id, nutrient_id}, а {ingredient_id, nutrient_id} → R12
ingredient_id nutrient_id quantity
21 1 10.0
22 2 5.0

R13 - RECIPE_CONTAINS_RESTRICTION

Проверка на кандидат-клучот: Чиста составна релациска табела со клуч {recipe_id, restriction_id}.

{ingredient_id, nutrient_id} → {nutrient_amount}

Одделена релација:

  • R13 ( recipe_id, restriction_id )

Остаточна релација:

R′13 ( ingredient_id, nutrient_id, restriction_id, recipe_id,
post_id, comment_id, list_id, planner_id, bio_id, buy_quantity )
  • lossless тест: R13 ∩ R′13 = {recipe_id, restriction_id}, а {recipe_id, restriction_id} → R13
recipe_id restriction_id
1 1
2 2

R14 - GROCERY_LIST_BULK_ADD_INGREDIENT

Проверка на кандидат-клучот: Чиста составна релациска табела со клуч {list_id, recipe_id}.

{list_id, recipe_id} → Нема атрибути надвор од клучот.

Одделена релација:

  • R14 ( list_id, recipe_id )

Остаточна релација:

R′14 ( ingredient_id, nutrient_id, restriction_id,
recipe_id, post_id, comment_id, list_id, planner_id, bio_id, buy_quantity )
  • lossless тест: R14 ∩ R′14 = {list_id, recipe_id}, а {list_id, recipe_id} → R14.
list_id recipe_id
1 3
2 4

R15 - GROCERY_LIST_SINGLE_ADD_INGREDIENT

Проверка на кандидат-клучот: Составен примарен клуч {list_id, ingredient_id}.

{list_id, ingredient_id} → {buy_quantity}

Одделена релација:

  • R15 ( list_id, ingredient_id, buy_quantity )

Остаточна релација:

R′15 ( ingredient_id, nutrient_id, restriction_id,
recipe_id, post_id, comment_id, list_id, planner_id, bio_id )
  • lossless тест: R15 ∩ R′15 = {list_id, ingredient_id}, а {list_id, ingredient_id} → R15
list_id ingredient_id buy_quantity
1 3 1000
2 4 250

R16 - INTAKE_PLANNER_SAVE_TO_LIST_POST

Проверка на кандидат-клучот: Чиста составна врзна табела со клуч {planner_id, post_id}.

{planner_id, post_id} → Нема атрибути надвор од клучот.

Одделена релација:

  • R16 ( planner_id, post_id )

Остаточна релација:

R′16 ( ingredient_id, nutrient_id, restriction_id,
recipe_id, post_id, comment_id, list_id, planner_id, bio_id )
  • lossless тест: R16 ∩ R′16 = {planner_id, post_id}, а {planner_id, post_id} → R16
planner_id post_id
1 3
2 4

ТРЕТА(3NF)

За да се постигне 2NF:
• 2NF +
• Не постојат транзитивни зависности.
• Секој атрибут надвор од клучот зависи строго и директно само од примарниот клуч.

Транзитивноста е разрешена со декомпозицијата во 2FN:

  • comment_id → user_id → email, recipe_id → user_id → username
  • post_id → recipe_id → kcal_sum

Ниеден атрибут надвор од клучот во ниедна релација не зависи од друг атрибут надвор од клучот, па шемата ја задоволува 3NF. Изведените атрибути за kcal и kcal_sum (R5, R8 и R9) можат да се гледаат како отстапување во овај случај, но тие се материјализираат при секое барање така што е повеќе отсликува редудентност меѓу релации. За подетална анализа посочете се кон DML скриптата за калкулации во Фаза 02.

БОЈС-КОДОВА (BCNF)

Релацијата е во BCNF ако за секоја нетривијална функционална зависност X → Y, X е суперклуч. Во секоја релација детерминантите се:
* самиот примарен клуч, кој по дефиниција е суперклуч;
* алтернативен клуч — email и username во R1, ingredient_name во R2, nutr_description во R3, restr_description во R4, recipe_id во R6, {user_id, bio_date} во R10. Секој од нив е UNIQUE и NOT NULL (освен recipe_id, види подолу), па секој е самиот кандидат-клуч, а со тоа и суперклуч.

POST.recipe_id е UNIQUE но дозволува NULL, бидејќи објавата може да биде обична фотографија без рецепт. Меѓу вредностите што не се NULL тој се однесува како кандидат-клуч, па recipe_id → post_id не воведува редундантност.

Финален преглед на шемата

Релација PK FK Нормална форма
USERuser_idX
INGREDIENTingredient_idX3NF / BCNF
NUTRIENTnutrient_id X3NF / BCNF
RESTRICTIONrestriction_idX3NF / BCNF
RECIPErecipe_iduser_id → USER 3NF / BCNF
POSTpost_iduser_id → USER, recipe_id → RECIPE3NF / BCNF
COMMENT comment_idpost_id → POST, user_id → USER3NF / BCNF
GROCERY_LISTlist_iduser_id → USER3NF / BCNF
INTAKE_PLANNERplanner_iduser_id → USER3NF / BCNF
BIOMETRICSbio_iduser_id → USER3NF / BCNF
RECIPE_CONTAINS_INGREDIENTrecipe_id, nutrient_idrecipe_id → RECIPE, ingredient_id → INGREDIENT3NF / BCNF
INGREDIENT_CONTAINS_NUTRIENTingredient_id, nutrient_idingredient_id → INGREDIENT, nutrient_id → NUTRIENT3NF / BCNF
RECIPE_CONTAINS_RESTRICTIONrecipe_id, restriction_id recipe_id → RECIPE, restriction_id → RESTRICTION3NF / BCNF
GROCERY_LIST_BULK_ADD_INGREDIENTlist_id, recipe_idlist_id → GROCERY_LIST, recipe_id → RECIPE3NF / BCNF
GROCERY_LIST_SINGLE_ADD_INGREDIENTlist_id, ingredient_idlist_id → GROCERY_LIST, ingredient_id → INGREDIENT3NF / BCNF
INTAKE_PLANNER_SAVE_TO_LIST_POSTplanner_id, post_idplanner_id → INTAKE_PLANNER, post_id → POST3NF / BCNF

Навигација низ проектот

Почетна страна

Фаза Име на фаза Статус
P0 Дефинирање проект Одобрен
P1 Концептуален дизајн и ЕР Дијаграм Одобрен
P2 Логички и физички дизајн - DDL Одобрен
P3 Кориснички/апикациски сценарија со базата на податоци Одобрен
P4 Протип со основни функционалности WIP
М1 Презентација на прототип WIP
P5 Нормализација и оптимизација на дизајн WIP
P6 Напредни извештаи од базата WIP
P7 Понапреден развој на базата WIP
P8 Напреден апликативен развој WIP
P9 Дополнителни имплементации WIP
М2 Финализиран проект WIP

(Trac навигација)

Note: See TracWiki for help on using the wiki.