wiki:Normalization

Version 22 (modified by 231175, 10 hours ago) ( diff )

--

Формална анализа и доказ за нормализација на Shifter

1. Дефиниција на универзалната релација

Тргнуваме од една единствена универзална релација која ги содржи сите атрибути од доменот.

Universal_Relation_Shifter( account_id, account_email, account_password_hash, account_name,
user_id, user_name, user_email, user_login_provider, user_password_hash, user_is_verified, user_is_profile_complete, user_has_used_free_consultation, user_company_size, user_work_position, user_points, user_favorite_course_ids,
meeting_reminder_id, meeting_at, meeting_scheduled_at, meeting_sent, meeting_link,
expert_id,
language_id, language_value,
course_id, course_image_url, course_color, course_difficulty, course_duration_minutes, course_price,
course_version_id, version_number, version_created_at, version_is_active,
enrollment_id, enrollment_status, enrollment_purchase_date, enrollment_activation_date, enrollment_completion_date,
payment_id, payment_amount, payment_date, payment_method, payment_status,
review_id, review_rating, review_comment, review_date,
course_translate_id, course_title_short, course_title, course_description_short, course_description, course_description_long,
course_what_will_be_learned,
course_content_id, content_position,
course_content_translate_id, content_translate_title,
course_lecture_id, lecture_duration_minutes, lecture_position, lecture_content_type,
course_lecture_translate_id, lecture_title, lecture_content_file_name, lecture_description, lecture_content_text,
user_course_progress_id, progress_is_completed, progress_completed_at,
verification_token_uuid, token_created_at, token_expires_at,
tag_id, tag_type,
tag_translate_id, tag_value )

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

Ги идентификуваме сите функционални зависности што важат во универзалната релација:

F = {

  • FD1: user_id → user_name, user_email, user_login_provider, user_password_hash, user_is_verified, user_is_profile_complete, user_has_used_free_consultation, user_company_size, user_work_position, user_points, account_id
  • FD2: verification_token_uuid → token_created_at, token_expires_at, user_id
  • FD3: meeting_reminder_id → meeting_at, meeting_scheduled_at, meeting_sent, meeting_link, user_id
  • FD4: expert_id → account_id
  • FD5: course_id → course_image_url, course_color, course_difficulty, course_duration_minutes, course_price
  • FD6: enrollment_id → enrollment_status, enrollment_purchase_date, enrollment_activation_date, enrollment_completion_date, user_id, course_version_id, payment_id, review_id
  • FD7: payment_id → payment_amount, payment_date, payment_method, payment_status, enrollment_id
  • FD8: review_id → review_rating, review_comment, review_date, enrollment_id
  • FD9: tag_id → tag_type
  • FD10: tag_translate_id → tag_value, tag_id, language_id
  • FD11: course_version_id → version_number, version_created_at, version_is_active, course_id
  • FD12: course_translate_id → course_title_short, course_title, course_description_short, course_description, course_description_long, course_id, language_id
  • FD13: course_content_id → content_position, course_version_id
  • FD14: course_content_translate_id → content_translate_title, course_content_id, language_id
  • FD15: course_lecture_id → lecture_duration_minutes, lecture_position, lecture_content_type, course_content_id
  • FD16: course_lecture_translate_id → lecture_title, lecture_content_file_name, lecture_description, lecture_content_text, course_lecture_id, language_id
  • FD17: user_course_progress_id → progress_is_completed, progress_completed_at, enrollment_id, course_lecture_id
  • FD18: user_email → user_id
  • FD19: account_id → account_email, account_password_hash, account_name
  • FD20: (course_id, version_number) → course_version_id
  • FD21: (course_id, language_id) → course_translate_id
  • FD22: (course_lecture_id, language_id) → course_lecture_translate_id
  • FD23: (course_content_id, language_id) → course_content_translate_id
  • FD24: (tag_id, language_id) → tag_translate_id
  • FD25: (enrollment_id, course_lecture_id) → user_course_progress_id
  • FD26: (course_version_id, content_position) → course_content_id
  • FD27: (course_content_id, lecture_position) → course_lecture_id
  • FD28: (course_translate_id, course_what_will_be_learned) → ∅
  • FD29: (user_id, user_favorite_course_ids) → ∅
  • FD30: account_email → account_id
  • FD31: language_id → language_value
  • FD32: language_value → language_id
  • FD33: meeting_link → meeting_reminder_id

}


3. Кандидат клучеви и примарен клуч

3.1 Определување на кандидат клучеви

За да најдеме кандидат клуч, треба да најдеме минимално множество атрибути чиј затворач ги содржи сите атрибути на релацијата.

3.2 Класификација на атрибути (Лева / Десна страна)

Атрибут Лева страна Десна страна Класификација
account_id ✓ (FD19) ✓ (FD1, FD4, FD30) и лево и десно
account_email ✓ (FD30) ✓ (FD19) и лево и десно
account_password_hash ✓ (FD19) десно
account_name ✓ (FD19) десно
user_id ✓ (FD1, FD29) ✓ (FD2, FD3, FD6, FD18) и лево и десно
user_name ✓ (FD1) десно
user_email ✓ (FD18) ✓ (FD1) и лево и десно
user_login_provider ✓ (FD1) десно
user_password_hash ✓ (FD1) десно
user_is_verified ✓ (FD1) десно
user_is_profile_complete ✓ (FD1) десно
user_has_used_free_consultation ✓ (FD1) десно
user_company_size ✓ (FD1) десно
user_work_position ✓ (FD1) десно
user_points ✓ (FD1) десно
user_favorite_course_ids ✓ (FD29) лево
verification_token_uuid ✓ (FD2) лево
token_created_at ✓ (FD2) десно
token_expires_at ✓ (FD2) десно
meeting_reminder_id ✓ (FD3) ✓ (FD33) и лево и десно
meeting_at ✓ (FD3) десно
meeting_scheduled_at ✓ (FD3) десно
meeting_sent ✓ (FD3) десно
meeting_link ✓ (FD33) ✓ (FD3) и лево и десно
expert_id ✓ (FD4) лево
language_id ✓ (FD21, FD22, FD23, FD24, FD31) ✓ (FD10, FD12, FD14, FD16, FD32) и лево и десно
language_value ✓ (FD32) ✓ (FD31) и лево и десно
course_id ✓ (FD5, FD20, FD21) ✓ (FD11, FD12) и лево и десно
course_image_url ✓ (FD5) десно
course_color ✓ (FD5) десно
course_difficulty ✓ (FD5) десно
course_duration_minutes ✓ (FD5) десно
course_price ✓ (FD5) десно
enrollment_id ✓ (FD6, FD25) ✓ (FD7, FD8, FD17) и лево и десно
enrollment_status ✓ (FD6) десно
enrollment_purchase_date ✓ (FD6) десно
enrollment_activation_date ✓ (FD6) десно
enrollment_completion_date ✓ (FD6) десно
payment_id ✓ (FD7) ✓ (FD6) и лево и десно
payment_amount ✓ (FD7) десно
payment_date ✓ (FD7) десно
payment_method ✓ (FD7) десно
payment_status ✓ (FD7) десно
review_id ✓ (FD8) ✓ (FD6) и лево и десно
review_rating ✓ (FD8) десно
review_comment ✓ (FD8) десно
review_date ✓ (FD8) десно
tag_id ✓ (FD9, FD24) ✓ (FD10) и лево и десно
tag_type ✓ (FD9) десно
tag_translate_id ✓ (FD10) ✓ (FD24) и лево и десно
tag_value ✓ (FD10) десно
course_version_id ✓ (FD11, FD26) ✓ (FD6, FD13, FD20) и лево и десно
version_number ✓ (FD20) ✓ (FD11) и лево и десно
version_created_at ✓ (FD11) десно
version_is_active ✓ (FD11) десно
course_translate_id ✓ (FD12) ✓ (FD21) и лево и десно
course_title_short ✓ (FD12) десно
course_title ✓ (FD12) десно
course_description_short ✓ (FD12) десно
course_description ✓ (FD12) десно
course_description_long ✓ (FD12) десно
course_what_will_be_learned ✓ (FD28) лево
course_content_id ✓ (FD13, FD23, FD27) ✓ (FD14, FD15, FD26) и лево и десно
content_position ✓ (FD26) ✓ (FD13) и лево и десно
course_content_translate_id ✓ (FD14) ✓ (FD23) и лево и десно
content_translate_title ✓ (FD14) десно
course_lecture_id ✓ (FD15, FD22, FD25) ✓ (FD16, FD17, FD27) и лево и десно
lecture_duration_minutes ✓ (FD15) десно
lecture_position ✓ (FD27) ✓ (FD15) и лево и десно
lecture_content_type ✓ (FD15) десно
course_lecture_translate_id ✓ (FD16) ✓ (FD22) и лево и десно
lecture_title ✓ (FD16) десно
lecture_content_file_name ✓ (FD16) десно
lecture_description ✓ (FD16) десно
lecture_content_text ✓ (FD16) десно
user_course_progress_id ✓ (FD17) ✓ (FD25) и лево и десно
progress_is_completed ✓ (FD17) десно
progress_completed_at ✓ (FD17) десно

3.3 Атрибути кои се појавуваат САМО на лева страна

  • verification_token_uuid - мора да биде дел од секој кандидат клуч
  • meeting_reminder_id - мора да биде дел од секој кандидат клуч (или meeting_link, FD33)
  • expert_id - мора да биде дел од секој кандидат клуч
  • course_what_will_be_learned - мора да биде дел од секој кандидат клуч
  • user_favorite_course_ids - мора да биде дел од секој кандидат клуч

3.4 Пресметка на затворач

Чекор 1: Започнуваме со {verification_token_uuid, meeting_reminder_id, expert_id, course_what_will_be_learned, user_favorite_course_ids} и пресметуваме затворач:

{verification_token_uuid, meeting_reminder_id, expert_id, course_what_will_be_learned, user_favorite_course_ids}⁺:
- Од FD2 (verification_token_uuid →): добиваме token_created_at, token_expires_at, user_id
- Од FD3 (meeting_reminder_id →): добиваме meeting_at, meeting_scheduled_at, meeting_sent, meeting_link, user_id
- Од FD1 (user_id →): добиваме user_name, user_email, user_login_provider, user_password_hash, user_is_verified, user_is_profile_complete, user_has_used_free_consultation, user_company_size, user_work_position, user_points, account_id
- Од FD4 (expert_id →): добиваме account_id
- Од FD19 (account_id →): добиваме account_email, account_password_hash, account_name
- Од FD18 (user_email →): добиваме user_id
- Од FD30 (account_email →): добиваме account_id

{verification_token_uuid, meeting_reminder_id, expert_id, course_what_will_be_learned, user_favorite_course_ids}⁺ ≠ Universal_Relation_Shifter

Недостасуваат: language_id, language_value, course_id, course_image_url, course_color, course_difficulty, course_duration_minutes, course_price, course_version_id, version_number, version_created_at, version_is_active, enrollment_id, enrollment_status, enrollment_purchase_date, enrollment_activation_date, enrollment_completion_date, payment_id, payment_amount, payment_date, payment_method, payment_status, review_id, review_rating, review_comment, review_date, course_translate_id, course_title_short, course_title, course_description_short, course_description, course_description_long, course_content_id, content_position, course_content_translate_id, content_translate_title, course_lecture_id, lecture_duration_minutes, lecture_position, lecture_content_type, course_lecture_translate_id, lecture_title, lecture_content_file_name, lecture_description, lecture_content_text, user_course_progress_id, progress_is_completed, progress_completed_at, tag_id, tag_type, tag_translate_id, tag_value

Чекор 2: Додаваме user_course_progress_id:

{verification_token_uuid, meeting_reminder_id, expert_id, course_what_will_be_learned, user_favorite_course_ids, user_course_progress_id}⁺:
- Сè од претходно, плус:
- Од FD17 (user_course_progress_id →): добиваме progress_is_completed, progress_completed_at, enrollment_id, course_lecture_id
- Од FD15 (course_lecture_id →): добиваме lecture_duration_minutes, lecture_position, lecture_content_type, course_content_id
- Од FD13 (course_content_id →): добиваме content_position, course_version_id
- Од FD6 (enrollment_id →): добиваме enrollment_status, enrollment_purchase_date, enrollment_activation_date, enrollment_completion_date, user_id, course_version_id, payment_id, review_id
- Од FD7 (payment_id →): добиваме payment_amount, payment_date, payment_method, payment_status, enrollment_id
- Од FD8 (review_id →): добиваме review_rating, review_comment, review_date, enrollment_id
- Од FD11 (course_version_id →): добиваме version_number, version_created_at, version_is_active, course_id
- Од FD5 (course_id →): добиваме course_image_url, course_color, course_difficulty, course_duration_minutes, course_price

{verification_token_uuid, meeting_reminder_id, expert_id, course_what_will_be_learned, user_favorite_course_ids, user_course_progress_id}⁺ ≠ Universal_Relation_Shifter

Недостасуваат: language_id, language_value, course_translate_id, course_title_short, course_title, course_description_short, course_description, course_description_long, course_content_translate_id, content_translate_title, course_lecture_translate_id, lecture_title, lecture_content_file_name, lecture_description, lecture_content_text, tag_id, tag_type, tag_translate_id, tag_value

Чекор 3: Додаваме course_lecture_translate_id, course_content_translate_id, course_translate_id, tag_translate_id:

{verification_token_uuid, meeting_reminder_id, expert_id, course_what_will_be_learned, user_favorite_course_ids, user_course_progress_id, course_lecture_translate_id, course_content_translate_id, course_translate_id, tag_translate_id}⁺:
- Сè од претходно, плус:
- Од FD16 (course_lecture_translate_id →): добиваме lecture_title, lecture_content_file_name, lecture_description, lecture_content_text, course_lecture_id, language_id
- Од FD14 (course_content_translate_id →): добиваме content_translate_title, course_content_id, language_id
- Од FD12 (course_translate_id →): добиваме course_title_short, course_title, course_description_short, course_description, course_description_long, course_id, language_id
- Од FD10 (tag_translate_id →): добиваме tag_value, tag_id, language_id
- Од FD31 (language_id →): добиваме language_value
- Од FD9 (tag_id →): добиваме tag_type
- Од FD28 ({course_translate_id, course_what_will_be_learned} →): добиваме ∅

{verification_token_uuid, meeting_reminder_id, expert_id, course_what_will_be_learned, user_favorite_course_ids, user_course_progress_id, course_lecture_translate_id, course_content_translate_id, course_translate_id, tag_translate_id}⁺ = Universal_Relation_Shifter (ги содржи сите атрибути) ✓

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

Подмножество Затворач = Universal_Relation_Shifter?
{verification_token_uuid, meeting_reminder_id, expert_id, course_what_will_be_learned, user_favorite_course_ids}⁺ Недостасуваат атрибути од user_course_progress ✗ НЕ
{verification_token_uuid, meeting_reminder_id, expert_id, course_what_will_be_learned, user_favorite_course_ids, user_course_progress_id}⁺ Недостасуваат атрибути од course_translate ✗ НЕ
{verification_token_uuid, meeting_reminder_id, expert_id, course_what_will_be_learned, user_favorite_course_ids, user_course_progress_id, course_translate_id}⁺ Недостасуваат атрибути од course_lecture_translate ✗ НЕ
{verification_token_uuid, meeting_reminder_id, expert_id, course_what_will_be_learned, user_favorite_course_ids, user_course_progress_id, course_translate_id, course_lecture_translate_id}⁺ Недостасуваат атрибути од course_content_translate ✗ НЕ
{verification_token_uuid, meeting_reminder_id, expert_id, course_what_will_be_learned, user_favorite_course_ids, user_course_progress_id, course_translate_id, course_lecture_translate_id, course_content_translate_id}⁺ Недостасуваат атрибути од tag_translate ✗ НЕ
{verification_token_uuid, meeting_link, expert_id, course_what_will_be_learned, user_favorite_course_ids, user_course_progress_id, course_lecture_translate_id, course_content_translate_id, course_translate_id, tag_translate_id}⁺ Сите атрибути се присутни ✓ ДА
{verification_token_uuid, meeting_reminder_id, expert_id, course_what_will_be_learned, user_favorite_course_ids, enrollment_id, course_lecture_id, course_lecture_translate_id, course_content_translate_id, course_translate_id, tag_translate_id} Сите атрибути се присутни ✓ ДА
{verification_token_uuid, meeting_reminder_id, expert_id, course_what_will_be_learned, user_favorite_course_ids, user_course_progress_id, course_lecture_translate_id, course_content_translate_id, course_translate_id, tag_translate_id}⁺ Сите атрибути се присутни ✓ ДА

Заклучок: {verification_token_uuid, meeting_reminder_id, expert_id, course_what_will_be_learned, user_favorite_course_ids, user_course_progress_id, course_lecture_translate_id, course_content_translate_id, course_translate_id, tag_translate_id} е минимален и е кандидат клуч.

3.6 Други кандидат клучеви

Дополнителен кандидат клуч кој го разгледував беше {verification_token_uuid, meeting_reminder_id, expert_id, course_what_will_be_learned, user_favorite_course_ids, enrollment_id, course_lecture_id, course_lecture_translate_id, course_content_translate_id, course_translate_id, tag_translate_id}, каде наместо user_course_progress_id присутни се enrollment_id и course_lecture_id. Меѓутоа овој кандидат клуч е поголем од тој со user_course_progress_id, па затоа не го земам во предвид при бирање на примарен клуч.

Втор кандидат клуч со иста големина се добива ако наместо meeting_reminder_id се земе meeting_link (FD33). Го избирам meeting_reminder_id бидејќи е сурогат клуч со фиксна должина, наспроти meeting_link кој е текстуален атрибут.

3.7 Избор на примарен клуч

Избран примарен клуч: {verification_token_uuid, meeting_reminder_id, expert_id, course_what_will_be_learned, user_favorite_course_ids, user_course_progress_id, course_lecture_translate_id, course_content_translate_id, course_translate_id, tag_translate_id}

Образложение: Ова е примарен клуч (минимален кандидат-клуч) бидејќи само со овие атрибути може да се добијат сите останати атрибути од универзалната релација преку затворање на функционалните зависности. Причината за големиот број атрибути во примарниот клуч е што универзалната релација содржи повеќе независни ентитети (корисници, експерти, курсеви, оцени, преводи, тагови) кои не се директно поврзани преку функционални зависности. Секој од овие независни ентитети бара најмалку еден идентификатор во клучот за да може да се пристапи до сите негови атрибути. Овој клуч е минимален - отстранувањето на било кој атрибут од него би довело до тоа да не може да се изведат некои од атрибутите во релацијата.


4. 1NF Декомпозиција

Чекор 4.1: Декомпозиција на релацијата Universal_Relation_Shifter

Нарушува 1NF: Релацијата не е во 1NF бидејќи атрибутот course_what_will_be_learned претставува повеќевредносен (multi-valued) атрибут. Секој course_translate може да има повеќе ставки од course_what_will_be_learned.

Декомпозиција:

Course_Translate_What_Will_Be_Learned(course_translate_id, course_what_will_be_learned)
   PK: {course_translate_id, course_what_will_be_learned}

Universal_Relation_Shifter_Base_1 = Universal_Relation_Shifter - {course_what_will_be_learned}

Universal_Relation_Shifter_Base_1(
account_id, account_email, account_password_hash, account_name,
user_id, user_name, user_email, user_login_provider, user_password_hash, user_is_verified, user_is_profile_complete, user_has_used_free_consultation, user_company_size, user_work_position, user_points, user_favorite_course_ids,
meeting_reminder_id, meeting_at, meeting_scheduled_at, meeting_sent, meeting_link,
expert_id,
language_id, language_value,
course_id, course_image_url, course_color, course_difficulty, course_duration_minutes, course_price,
course_version_id, version_number, version_created_at, version_is_active,
enrollment_id, enrollment_status, enrollment_purchase_date, enrollment_activation_date, enrollment_completion_date,
payment_id, payment_amount, payment_date, payment_method, payment_status,
review_id, review_rating, review_comment, review_date,
course_translate_id, course_title_short, course_title, course_description_short, course_description, course_description_long,
course_content_id, content_position,
course_content_translate_id, content_translate_title,
course_lecture_id, lecture_duration_minutes, lecture_position, lecture_content_type,
course_lecture_translate_id, lecture_title, lecture_content_file_name, lecture_description, lecture_content_text,
user_course_progress_id, progress_is_completed, progress_completed_at,
verification_token_uuid, token_created_at, token_expires_at,
tag_id, tag_type,
tag_translate_id, tag_value
)
   PK: {verification_token_uuid, meeting_reminder_id, expert_id, user_favorite_course_ids, user_course_progress_id, course_lecture_translate_id, course_content_translate_id, course_translate_id, tag_translate_id}

Проверка за lossless join: Може да се реконструира преку course_translate_id

Проверка за dependency preservation: FD28 е зачувана во Course_Translate_What_Will_Be_Learned

Чекор 4.2: Декомпозиција на релацијата Universal_Relation_Shifter

Нарушува 1NF: Релацијата не е во 1NF бидејќи атрибутот user_favorite_course_ids претставува повеќевредносен (multi-valued) атрибут. Секој user може да има повеќе ставки од user_favorite_course_ids.

Декомпозиција:

User_Favorite_Course(user_id, course_id)
   PK: {user_id, course_id}

Universal_Relation_Shifter_Base = Universal_Relation_Shifter_Base_1 - {user_favorite_course_ids}

Universal_Relation_Shifter_Base(
account_id, account_email, account_password_hash, account_name,
user_id, user_name, user_email, user_login_provider, user_password_hash, user_is_verified, user_is_profile_complete, user_has_used_free_consultation, user_company_size, user_work_position, user_points,
meeting_reminder_id, meeting_at, meeting_scheduled_at, meeting_sent, meeting_link,
expert_id,
language_id, language_value,
course_id, course_image_url, course_color, course_difficulty, course_duration_minutes, course_price,
course_version_id, version_number, version_created_at, version_is_active,
enrollment_id, enrollment_status, enrollment_purchase_date, enrollment_activation_date, enrollment_completion_date,
payment_id, payment_amount, payment_date, payment_method, payment_status,
review_id, review_rating, review_comment, review_date,
course_translate_id, course_title_short, course_title, course_description_short, course_description, course_description_long,
course_content_id, content_position,
course_content_translate_id, content_translate_title,
course_lecture_id, lecture_duration_minutes, lecture_position, lecture_content_type,
course_lecture_translate_id, lecture_title, lecture_content_file_name, lecture_description, lecture_content_text,
user_course_progress_id, progress_is_completed, progress_completed_at,
verification_token_uuid, token_created_at, token_expires_at,
tag_id, tag_type,
tag_translate_id, tag_value
)
   PK: {verification_token_uuid, meeting_reminder_id, expert_id, user_course_progress_id, course_lecture_translate_id, course_content_translate_id, course_translate_id, tag_translate_id}

Проверка за lossless join: Може да се реконструира преку user_id

Проверка за dependency preservation: FD29 е зачувана во User_Favorite_Course

4.3 Проверка која FD ја нарушува 2NF

За да биде во 2NF, секој не-клучен атрибут мора целосно да зависи од примарниот клуч (нема парцијални зависности).

Примарен клуч во Universal_Relation_Shifter_Base: {verification_token_uuid, meeting_reminder_id, expert_id, user_course_progress_id, course_lecture_translate_id, course_content_translate_id, course_translate_id, tag_translate_id}

Парцијални зависности (атрибути кои зависат од дел од клучот):

FD Детерминант Дел од клуч? Парцијална зависност?
FD1: user_id → user_*, account_id user_id НЕ (транзитивно преку user_course_progress_id) Не директно
FD2: verification_token_uuid → token_created_at, token_expires_at, user_id verification_token_uuid ДА (дел од примарен клуч) ДА
FD3: meeting_reminder_id → meeting_at, meeting_scheduled_at, meeting_sent, meeting_link, user_id meeting_reminder_id ДА (дел од примарен клуч) ДА
FD4: expert_id → account_id expert_id ДА (дел од примарен клуч) ДА
FD5: course_id → course_image_url, course_color, course_difficulty, course_duration_minutes, course_price course_id НЕ (транзитивно преку user_course_progress_id) Не директно
FD6: enrollment_id → enrollment_*, user_id, course_version_id, payment_id, review_id enrollment_id НЕ (транзитивно преку user_course_progress_id) Не директно
FD7: payment_id → payment_amount, payment_date, payment_method, payment_status, enrollment_id payment_id НЕ (транзитивно преку user_course_progress_id) Не директно
FD8: review_id → review_rating, review_comment, review_date, enrollment_id review_id НЕ (транзитивно преку user_course_progress_id) Не директно
FD9: tag_id → tag_type tag_id НЕ (транзитивно преку tag_translate_id) Не директно
FD10: tag_translate_id → tag_value, tag_id, language_id tag_translate_id ДА (дел од примарен клуч) ДА
FD11: course_version_id → version_number, version_created_at, version_is_active, course_id course_version_id НЕ (транзитивно преку user_course_progress_id) Не директно
FD12: course_translate_id → course_title_short, course_title, course_description_short, course_description, course_description_long, course_id, language_id course_translate_id ДА (дел од примарен клуч) ДА
FD13: course_content_id → content_position, course_version_id course_content_id НЕ (транзитивно преку course_content_translate_id) Не директно
FD14: course_content_translate_id → content_translate_title, course_content_id, language_id course_content_translate_id ДА (дел од примарен клуч) ДА
FD15: course_lecture_id → lecture_duration_minutes, lecture_position, lecture_content_type, course_content_id course_lecture_id НЕ (транзитивно преку course_lecture_translate_id) Не директно
FD16: course_lecture_translate_id → lecture_title, lecture_content_file_name, lecture_description, lecture_content_text, course_lecture_id, language_id course_lecture_translate_id ДА (дел од примарен клуч) ДА
FD17: user_course_progress_id → progress_is_completed, progress_completed_at, enrollment_id, course_lecture_id user_course_progress_id ДА (дел од примарен клуч) ДА
FD18: user_email → user_id user_email НЕ Не
FD19: account_id → account_email, account_password_hash, account_name account_id НЕ (транзитивно преку expert_id и user_id) Не директно
FD20: (course_id, version_number) → course_version_id course_id, version_number НЕ Не
FD21: (course_id, language_id) → course_translate_id course_id, language_id НЕ Не
FD22: (course_lecture_id, language_id) → course_lecture_translate_id course_lecture_id, language_id НЕ Не
FD23: (course_content_id, language_id) → course_content_translate_id course_content_id, language_id НЕ Не
FD24: (tag_id, language_id) → tag_translate_id tag_id, language_id НЕ Не
FD25: (enrollment_id, course_lecture_id) → user_course_progress_id enrollment_id, course_lecture_id НЕ Не
FD26: (course_version_id, content_position) → course_content_id course_version_id, content_position НЕ Не
FD27: (course_content_id, lecture_position) → course_lecture_id course_content_id, lecture_position НЕ Не
FD30: account_email → account_id account_email НЕ Не
FD31: language_id → language_value language_id НЕ (транзитивно преку course_translate_id) Не директно
FD32: language_value → language_id language_value НЕ Не
FD33: meeting_link → meeting_reminder_id meeting_link НЕ Не

Заклучок: Релацијата не е во 2NF бидејќи има парцијални зависности. Атрибутите на FD2, FD3, FD4, FD10, FD12, FD14, FD16, FD17 зависат само од дел од примарниот клуч, а не од целиот клуч.

Идентификувани парцијални зависности што ја нарушуваат 2NF:

  1. FD2: verification_token_uuid → token_created_at, token_expires_at, user_id
  2. FD3: meeting_reminder_id → meeting_at, meeting_scheduled_at, meeting_sent, meeting_link, user_id
  3. FD4: expert_id → account_id
  4. FD10: tag_translate_id → tag_value, tag_id, language_id
  5. FD12: course_translate_id → course_title_short, course_title, course_description_short, course_description, course_description_long, course_id, language_id
  6. FD14: course_content_translate_id → content_translate_title, course_content_id, language_id
  7. FD16: course_lecture_translate_id → lecture_title, lecture_content_file_name, lecture_description, lecture_content_text, course_lecture_id, language_id
  8. FD17: user_course_progress_id → progress_is_completed, progress_completed_at, enrollment_id, course_lecture_id

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

Чекор 5.1: Декомпозиција по FD2 (verification_token_uuid → ...)

Нарушува 2NF: verification_token_uuid е дел од примарниот клуч, а token_created_at, token_expires_at зависат само од verification_token_uuid.

Декомпозиција:

Verification_Token(verification_token_uuid, token_created_at, token_expires_at, user_id)
   PK: verification_token_uuid

Universal_Relation_Shifter_Base_1 = Universal_Relation_Shifter_Base - {verification_token_uuid, token_created_at, token_expires_at}

Universal_Relation_Shifter_Base_1(
account_id, account_email, account_password_hash, account_name,
user_id, user_name, user_email, user_login_provider, user_password_hash, user_is_verified, user_is_profile_complete, user_has_used_free_consultation, user_company_size, user_work_position, user_points,
meeting_reminder_id, meeting_at, meeting_scheduled_at, meeting_sent, meeting_link,
expert_id,
language_id, language_value,
course_id, course_image_url, course_color, course_difficulty, course_duration_minutes, course_price,
course_version_id, version_number, version_created_at, version_is_active,
enrollment_id, enrollment_status, enrollment_purchase_date, enrollment_activation_date, enrollment_completion_date,
payment_id, payment_amount, payment_date, payment_method, payment_status,
review_id, review_rating, review_comment, review_date,
course_translate_id, course_title_short, course_title, course_description_short, course_description, course_description_long,
course_content_id, content_position,
course_content_translate_id, content_translate_title,
course_lecture_id, lecture_duration_minutes, lecture_position, lecture_content_type,
course_lecture_translate_id, lecture_title, lecture_content_file_name, lecture_description, lecture_content_text,
user_course_progress_id, progress_is_completed, progress_completed_at,
tag_id, tag_type,
tag_translate_id, tag_value
)
   PK: {meeting_reminder_id, expert_id, user_course_progress_id, course_lecture_translate_id, course_content_translate_id, course_translate_id, tag_translate_id}

Проверка за lossless join: Може да се реконструира преку user_id

Проверка за dependency preservation: FD2 е зачувана во Verification_Token

Чекор 5.2: Декомпозиција по FD3 (meeting_reminder_id → ...)

Нарушува 2NF: meeting_reminder_id е дел од примарниот клуч, а meeting_at, meeting_scheduled_at, meeting_sent, meeting_link зависат само од meeting_reminder_id.

Декомпозиција:

Meeting_Reminder(meeting_reminder_id, meeting_at, meeting_scheduled_at, meeting_sent, meeting_link, user_id)
   PK: meeting_reminder_id

Universal_Relation_Shifter_Base_2 = Universal_Relation_Shifter_Base_1 - {meeting_reminder_id, meeting_at, meeting_scheduled_at, meeting_sent, meeting_link}

Universal_Relation_Shifter_Base_2(
account_id, account_email, account_password_hash, account_name,
user_id, user_name, user_email, user_login_provider, user_password_hash, user_is_verified, user_is_profile_complete, user_has_used_free_consultation, user_company_size, user_work_position, user_points,
expert_id,
language_id, language_value,
course_id, course_image_url, course_color, course_difficulty, course_duration_minutes, course_price,
course_version_id, version_number, version_created_at, version_is_active,
enrollment_id, enrollment_status, enrollment_purchase_date, enrollment_activation_date, enrollment_completion_date,
payment_id, payment_amount, payment_date, payment_method, payment_status,
review_id, review_rating, review_comment, review_date,
course_translate_id, course_title_short, course_title, course_description_short, course_description, course_description_long,
course_content_id, content_position,
course_content_translate_id, content_translate_title,
course_lecture_id, lecture_duration_minutes, lecture_position, lecture_content_type,
course_lecture_translate_id, lecture_title, lecture_content_file_name, lecture_description, lecture_content_text,
user_course_progress_id, progress_is_completed, progress_completed_at,
tag_id, tag_type,
tag_translate_id, tag_value
)
   PK: {expert_id, user_course_progress_id, course_lecture_translate_id, course_content_translate_id, course_translate_id, tag_translate_id}

Проверка за lossless join: Може да се реконструира преку user_id Проверка за dependency preservation: FD3 и FD33 се зачувани во Meeting_Reminder

Чекор 5.3: Декомпозиција по FD4 (expert_id → ...)

Нарушува 2NF: expert_id е дел од примарниот клуч, а account_id зависи само од expert_id.

Декомпозиција:

Expert(expert_id, account_id)
   PK: expert_id

Universal_Relation_Shifter_Base_3 = Universal_Relation_Shifter_Base_2 - {expert_id}

Universal_Relation_Shifter_Base_3(
account_id, account_email, account_password_hash, account_name,
user_id, user_name, user_email, user_login_provider, user_password_hash, user_is_verified, user_is_profile_complete, user_has_used_free_consultation, user_company_size, user_work_position, user_points,
language_id, language_value,
course_id, course_image_url, course_color, course_difficulty, course_duration_minutes, course_price,
course_version_id, version_number, version_created_at, version_is_active,
enrollment_id, enrollment_status, enrollment_purchase_date, enrollment_activation_date, enrollment_completion_date,
payment_id, payment_amount, payment_date, payment_method, payment_status,
review_id, review_rating, review_comment, review_date,
course_translate_id, course_title_short, course_title, course_description_short, course_description, course_description_long,
course_content_id, content_position,
course_content_translate_id, content_translate_title,
course_lecture_id, lecture_duration_minutes, lecture_position, lecture_content_type,
course_lecture_translate_id, lecture_title, lecture_content_file_name, lecture_description, lecture_content_text,
user_course_progress_id, progress_is_completed, progress_completed_at,
tag_id, tag_type,
tag_translate_id, tag_value
)
   PK: {user_course_progress_id, course_lecture_translate_id, course_content_translate_id, course_translate_id, tag_translate_id}

Проверка за lossless join: Може да се реконструира преку account_id Проверка за dependency preservation: FD4 е зачувана во Expert

Чекор 5.4: Декомпозиција по FD10 (tag_translate_id → ...)

Нарушува 2NF: tag_translate_id е дел од примарниот клуч, а tag_value зависи само од tag_translate_id.

Декомпозиција:

Tag_Translate(tag_translate_id, tag_value, tag_id, language_id)
   PK: tag_translate_id

Universal_Relation_Shifter_Base_4 = Universal_Relation_Shifter_Base_3 - {tag_translate_id, tag_value}

Universal_Relation_Shifter_Base_4(
account_id, account_email, account_password_hash, account_name,
user_id, user_name, user_email, user_login_provider, user_password_hash, user_is_verified, user_is_profile_complete, user_has_used_free_consultation, user_company_size, user_work_position, user_points,
language_id, language_value,
course_id, course_image_url, course_color, course_difficulty, course_duration_minutes, course_price,
course_version_id, version_number, version_created_at, version_is_active,
enrollment_id, enrollment_status, enrollment_purchase_date, enrollment_activation_date, enrollment_completion_date,
payment_id, payment_amount, payment_date, payment_method, payment_status,
review_id, review_rating, review_comment, review_date,
course_translate_id, course_title_short, course_title, course_description_short, course_description, course_description_long,
course_content_id, content_position,
course_content_translate_id, content_translate_title,
course_lecture_id, lecture_duration_minutes, lecture_position, lecture_content_type,
course_lecture_translate_id, lecture_title, lecture_content_file_name, lecture_description, lecture_content_text,
user_course_progress_id, progress_is_completed, progress_completed_at,
tag_id, tag_type
)
   PK: {user_course_progress_id, course_lecture_translate_id, course_content_translate_id, course_translate_id}

Проверка за lossless join: Може да се реконструира преку tag_id Проверка за dependency preservation: FD10 е зачувана во Tag_Translate

Чекор 5.5: Декомпозиција по FD12 (course_translate_id → ...)

Нарушува 2NF: course_translate_id е дел од примарниот клуч, а course_title_short, course_title, course_description_short, course_description, course_description_long зависат само од course_translate_id.

Декомпозиција:

Course_Translate(course_translate_id, course_title_short, course_title, course_description_short, course_description, course_description_long, course_id, language_id)
   PK: course_translate_id

Universal_Relation_Shifter_Base_5 = Universal_Relation_Shifter_Base_4 - {course_translate_id, course_title_short, course_title, course_description_short, course_description, course_description_long}

Universal_Relation_Shifter_Base_5(
account_id, account_email, account_password_hash, account_name,
user_id, user_name, user_email, user_login_provider, user_password_hash, user_is_verified, user_is_profile_complete, user_has_used_free_consultation, user_company_size, user_work_position, user_points,
language_id, language_value,
course_id, course_image_url, course_color, course_difficulty, course_duration_minutes, course_price,
course_version_id, version_number, version_created_at, version_is_active,
enrollment_id, enrollment_status, enrollment_purchase_date, enrollment_activation_date, enrollment_completion_date,
payment_id, payment_amount, payment_date, payment_method, payment_status,
review_id, review_rating, review_comment, review_date,
course_content_id, content_position,
course_content_translate_id, content_translate_title,
course_lecture_id, lecture_duration_minutes, lecture_position, lecture_content_type,
course_lecture_translate_id, lecture_title, lecture_content_file_name, lecture_description, lecture_content_text,
user_course_progress_id, progress_is_completed, progress_completed_at,
tag_id, tag_type
)
   PK: {user_course_progress_id, course_lecture_translate_id, course_content_translate_id}

Проверка за lossless join: Може да се реконструира преку course_id Проверка за dependency preservation: FD12 е зачувана во Course_Translate

Чекор 5.6: Декомпозиција по FD14 (course_content_translate_id → ...)

Нарушува 2NF: course_content_translate_id е дел од примарниот клуч, а content_translate_title зависи само од course_content_translate_id.

Декомпозиција:

Course_Content_Translate(course_content_translate_id, content_translate_title, course_content_id, language_id)
   PK: course_content_translate_id

Universal_Relation_Shifter_Base_6 = Universal_Relation_Shifter_Base_5 - {course_content_translate_id, content_translate_title}

Universal_Relation_Shifter_Base_6(
account_id, account_email, account_password_hash, account_name,
user_id, user_name, user_email, user_login_provider, user_password_hash, user_is_verified, user_is_profile_complete, user_has_used_free_consultation, user_company_size, user_work_position, user_points,
language_id, language_value,
course_id, course_image_url, course_color, course_difficulty, course_duration_minutes, course_price,
course_version_id, version_number, version_created_at, version_is_active,
enrollment_id, enrollment_status, enrollment_purchase_date, enrollment_activation_date, enrollment_completion_date,
payment_id, payment_amount, payment_date, payment_method, payment_status,
review_id, review_rating, review_comment, review_date,
course_content_id, content_position,
course_lecture_id, lecture_duration_minutes, lecture_position, lecture_content_type,
course_lecture_translate_id, lecture_title, lecture_content_file_name, lecture_description, lecture_content_text,
user_course_progress_id, progress_is_completed, progress_completed_at,
tag_id, tag_type
)
   PK: {user_course_progress_id, course_lecture_translate_id}

Проверка за lossless join: Може да се реконструира преку course_content_id Проверка за dependency preservation: FD14 е зачувана во Course_Content_Translate

Чекор 5.7: Декомпозиција по FD16 (course_lecture_translate_id → ...)

Нарушува 2NF: course_lecture_translate_id е дел од примарниот клуч, а lecture_title, lecture_content_file_name, lecture_description, lecture_content_text зависат само од course_lecture_translate_id.

Декомпозиција:

Course_Lecture_Translate(course_lecture_translate_id, lecture_title, lecture_content_file_name, lecture_description, lecture_content_text, course_lecture_id, language_id)
   PK: course_lecture_translate_id

Universal_Relation_Shifter_Base_7 = Universal_Relation_Shifter_Base_6 - {course_lecture_translate_id, lecture_title, lecture_content_file_name, lecture_description, lecture_content_text}

Universal_Relation_Shifter_Base_7(
account_id, account_email, account_password_hash, account_name,
user_id, user_name, user_email, user_login_provider, user_password_hash, user_is_verified, user_is_profile_complete, user_has_used_free_consultation, user_company_size, user_work_position, user_points,
language_id, language_value,
course_id, course_image_url, course_color, course_difficulty, course_duration_minutes, course_price,
course_version_id, version_number, version_created_at, version_is_active,
enrollment_id, enrollment_status, enrollment_purchase_date, enrollment_activation_date, enrollment_completion_date,
payment_id, payment_amount, payment_date, payment_method, payment_status,
review_id, review_rating, review_comment, review_date,
course_content_id, content_position,
course_lecture_id, lecture_duration_minutes, lecture_position, lecture_content_type,
user_course_progress_id, progress_is_completed, progress_completed_at,
tag_id, tag_type
)
   PK: {user_course_progress_id}

Проверка за lossless join: Може да се реконструира преку course_lecture_id Проверка за dependency preservation: FD16 е зачувана во Course_Lecture_Translate

Чекор 5.8: Декомпозиција по FD17 (user_course_progress_id → ...)

Нарушува 2NF: user_course_progress_id е дел од примарниот клуч, а progress_is_completed, progress_completed_at зависат само од user_course_progress_id.

Декомпозиција:

User_Course_Progress(user_course_progress_id, progress_is_completed, progress_completed_at, enrollment_id, course_lecture_id)
   PK: user_course_progress_id

Universal_Relation_Shifter_Base_8 = Universal_Relation_Shifter_Base_7 - {user_course_progress_id, progress_is_completed, progress_completed_at}

Universal_Relation_Shifter_Base_8(
account_id, account_email, account_password_hash, account_name,
user_id, user_name, user_email, user_login_provider, user_password_hash, user_is_verified, user_is_profile_complete, user_has_used_free_consultation, user_company_size, user_work_position, user_points,
language_id, language_value,
course_id, course_image_url, course_color, course_difficulty, course_duration_minutes, course_price,
course_version_id, version_number, version_created_at, version_is_active,
enrollment_id, enrollment_status, enrollment_purchase_date, enrollment_activation_date, enrollment_completion_date,
payment_id, payment_amount, payment_date, payment_method, payment_status,
review_id, review_rating, review_comment, review_date,
course_content_id, content_position,
course_lecture_id, lecture_duration_minutes, lecture_position, lecture_content_type,
tag_id, tag_type
)
   PK: ∅

Проверка за lossless join: Може да се реконструира преку enrollment_id и course_lecture_id Проверка за dependency preservation: FD17 е зачувана во User_Course_Progress

Состојба после 2NF декомпозиција

User_Favorite_Course(user_id, course_id)
   PK: {user_id, course_id}

Course_Translate_What_Will_Be_Learned(course_translate_id, course_what_will_be_learned)
   PK: {course_translate_id, course_what_will_be_learned}

Verification_Token(verification_token_uuid, token_created_at, token_expires_at, user_id)
   PK: verification_token_uuid

Meeting_Reminder(meeting_reminder_id, meeting_at, meeting_scheduled_at, meeting_sent, meeting_link, user_id)
   PK: meeting_reminder_id

Expert(expert_id, account_id)
   PK: expert_id

Tag_Translate(tag_translate_id, tag_value, tag_id, language_id)
   PK: tag_translate_id

Course_Translate(course_translate_id, course_title_short, course_title, course_description_short, course_description, course_description_long, course_id, language_id)
   PK: course_translate_id

Course_Content_Translate(course_content_translate_id, content_translate_title, course_content_id, language_id)
   PK: course_content_translate_id

Course_Lecture_Translate(course_lecture_translate_id, lecture_title, lecture_content_file_name, lecture_description, lecture_content_text, course_lecture_id, language_id)
   PK: course_lecture_translate_id

User_Course_Progress(user_course_progress_id, progress_is_completed, progress_completed_at, enrollment_id, course_lecture_id)
   PK: user_course_progress_id

Universal_Relation_Shifter_Base_8(
account_id, account_email, account_password_hash, account_name,
user_id, user_name, user_email, user_login_provider, user_password_hash, user_is_verified, user_is_profile_complete, user_has_used_free_consultation, user_company_size, user_work_position, user_points,
language_id, language_value,
course_id, course_image_url, course_color, course_difficulty, course_duration_minutes, course_price,
course_version_id, version_number, version_created_at, version_is_active,
enrollment_id, enrollment_status, enrollment_purchase_date, enrollment_activation_date, enrollment_completion_date,
payment_id, payment_amount, payment_date, payment_method, payment_status,
review_id, review_rating, review_comment, review_date,
course_content_id, content_position,
course_lecture_id, lecture_duration_minutes, lecture_position, lecture_content_type,
tag_id, tag_type
)
   PK: ∅

6. 3NF Декомпозиција

Проверка за транзитивни зависности во секоја релација

Verification_Token(verification_token_uuid, token_created_at, token_expires_at, user_id)

Транзитивни зависности:

  • verification_token_uuid → user_id → user_*?
    • user_* НЕ се во Verification_Token, па нема транзитивна зависност во оваа релација.

Заклучок: Verification_Token е во 3NF ✓

Meeting_Reminder(meeting_reminder_id, meeting_at, meeting_scheduled_at, meeting_sent, meeting_link, user_id)

Транзитивни зависности:

  • meeting_reminder_id → user_id → user_*?
    • user_* атрибутите НЕ се во Meeting_Reminder.

Заклучок: Meeting_Reminder е во 3NF ✓

Expert(expert_id, account_id)

Транзитивни зависности:

  • expert_id → account_id → account_*?
    • account_* атрибутите НЕ се во Expert.

Заклучок: Expert е во 3NF ✓

Tag_Translate(tag_translate_id, tag_value, tag_id, language_id)

Транзитивни зависности:

  • tag_translate_id → tag_id → tag_*?
    • tag_* атрибутите НЕ се во Tag_Translate.
  • tag_translate_id → language_id → language_value?
    • language_value НЕ е во Tag_Translate.

Заклучок: Tag_Translate е во 3NF ✓

Course_Translate(course_translate_id, course_title_short, course_title, course_description_short, course_description, course_description_long, course_id, language_id)

Транзитивни зависности:

  • course_translate_id → course_id → course_*?
    • course_* атрибутите НЕ се во Course_Translate.
  • course_translate_id → language_id → language_value?
    • language_value НЕ е во Course_Translate.

Заклучок: Course_Translate е во 3NF ✓

Course_Content_Translate(course_content_translate_id, content_translate_title, course_content_id, language_id)

Транзитивни зависности:

  • course_content_translate_id → course_content_id → course_content_*?
    • course_content_* атрибутите НЕ се во Course_Content_Translate.
  • course_content_translate_id → language_id → language_value?
    • language_value НЕ е во Course_Content_Translate.

Заклучок: Course_Content_Translate е во 3NF ✓

Course_Lecture_Translate(course_lecture_translate_id, lecture_title, lecture_content_file_name, lecture_description, lecture_content_text, course_lecture_id, language_id)

Транзитивни зависности:

  • course_lecture_translate_id → course_lecture_id → course_lecture_*?
    • course_lecture_* атрибутите НЕ се во Course_Lecture_Translate.
  • course_lecture_translate_id → language_id → language_value?
    • language_value НЕ е во Course_Lecture_Translate.

Заклучок: Course_Lecture_Translate е во 3NF ✓

User_Course_Progress(user_course_progress_id, progress_is_completed, progress_completed_at, enrollment_id, course_lecture_id)

Транзитивни зависности:

  • user_course_progress_id → enrollment_id → enrollment_*?
    • enrollment_* атрибутите НЕ се во User_Course_Progress.
  • user_course_progress_id → course_lecture_id → course_lecture_*?
    • course_lecture_* атрибутите НЕ се во User_Course_Progress.

Заклучок: User_Course_Progress е во 3NF ✓

Universal_Relation_Shifter_Base_8 - Проверка за транзитивни зависности

Примарен клуч на Universal_Relation_Shifter_Base_8:

Заклучок: После извлекување на сите парцијални зависности (FD2, FD3, FD4, FD10, FD12, FD14, FD16, FD17), универзалната релација е целосно декомпонирана. Примарниот клуч на Universal_Relation_Shifter_Base_8 е празно множество (∅), што значи дека таа релација повеќе не постои како валидна релација. Останатите функционални зависности (FD1, FD5-FD9, FD11, FD13, FD15, FD19, FD31) нарушуваат 3NF бидејќи нивните детерминанти (user_id, course_id, enrollment_id, account_id, language_id, итн.) не се примарни клучеви на Universal_Relation_Shifter_Base_8, туку се независни детерминанти кои треба да формираат посебни релации.

Чекор 6.1: Декомпозиција по FD1 (user_id → user_*)

User(user_id, user_name, user_email, user_login_provider, user_password_hash, user_is_verified, user_is_profile_complete, user_has_used_free_consultation, user_company_size, user_work_position, user_points, account_id)
   PK: user_id

Universal_Relation_Shifter_Base_9 = Universal_Relation_Shifter_Base_8 - {user_id, user_name, user_email, user_login_provider, user_password_hash, user_is_verified, user_is_profile_complete, user_has_used_free_consultation, user_company_size, user_work_position, user_points}

Universal_Relation_Shifter_Base_9(
account_id, account_email, account_password_hash, account_name,
language_id, language_value,
course_id, course_image_url, course_color, course_difficulty, course_duration_minutes, course_price,
course_version_id, version_number, version_created_at, version_is_active,
enrollment_id, enrollment_status, enrollment_purchase_date, enrollment_activation_date, enrollment_completion_date,
payment_id, payment_amount, payment_date, payment_method, payment_status,
review_id, review_rating, review_comment, review_date,
course_content_id, content_position,
course_lecture_id, lecture_duration_minutes, lecture_position, lecture_content_type,
tag_id, tag_type
)

Проверка за lossless join: Може да се реконструира преку enrollment_id Проверка за dependency preservation: FD1 и FD18 се зачувани во User

Чекор 6.2: Декомпозиција по FD5 (course_id → course_*)

Course(course_id, course_image_url, course_color, course_difficulty, course_duration_minutes, course_price)
   PK: course_id

Universal_Relation_Shifter_Base_10 = Universal_Relation_Shifter_Base_9 - {course_id, course_image_url, course_color, course_difficulty, course_duration_minutes, course_price}

Universal_Relation_Shifter_Base_10(
account_id, account_email, account_password_hash, account_name,
language_id, language_value,
course_version_id, version_number, version_created_at, version_is_active,
enrollment_id, enrollment_status, enrollment_purchase_date, enrollment_activation_date, enrollment_completion_date,
payment_id, payment_amount, payment_date, payment_method, payment_status,
review_id, review_rating, review_comment, review_date,
course_content_id, content_position,
course_lecture_id, lecture_duration_minutes, lecture_position, lecture_content_type,
tag_id, tag_type
)

Проверка за lossless join: Може да се реконструира преку enrollment_id → course_version_id Проверка за dependency preservation: FD5 е зачувана во Course

Чекор 6.3: Декомпозиција по FD6 (enrollment_id → enrollment_*)

Enrollment(enrollment_id, enrollment_status, enrollment_purchase_date, enrollment_activation_date, enrollment_completion_date, user_id, course_version_id)
   PK: enrollment_id

Universal_Relation_Shifter_Base_11 = Universal_Relation_Shifter_Base_10 - {enrollment_status, enrollment_purchase_date, enrollment_activation_date, enrollment_completion_date}

Universal_Relation_Shifter_Base_11(
account_id, account_email, account_password_hash, account_name,
language_id, language_value,
course_version_id, version_number, version_created_at, version_is_active,
enrollment_id,
payment_id, payment_amount, payment_date, payment_method, payment_status,
review_id, review_rating, review_comment, review_date,
course_content_id, content_position,
course_lecture_id, lecture_duration_minutes, lecture_position, lecture_content_type,
tag_id, tag_type
)

Проверка за lossless join: Може да се реконструира преку enrollment_id Проверка за dependency preservation: FD6 е зачувана во Enrollment

Чекор 6.4: Декомпозиција по FD7 (payment_id → payment_*)

Payment(payment_id, payment_amount, payment_date, payment_method, payment_status, enrollment_id)
   PK: payment_id

Universal_Relation_Shifter_Base_12 = Universal_Relation_Shifter_Base_11 - {payment_id, payment_amount, payment_date, payment_method, payment_status}

Universal_Relation_Shifter_Base_12(
account_id, account_email, account_password_hash, account_name,
language_id, language_value,
course_version_id, version_number, version_created_at, version_is_active,
enrollment_id,
review_id, review_rating, review_comment, review_date,
course_content_id, content_position,
course_lecture_id, lecture_duration_minutes, lecture_position, lecture_content_type,
tag_id, tag_type
)

Проверка за lossless join: Може да се реконструира преку enrollment_id Проверка за dependency preservation: FD7 е зачувана во Payment

Чекор 6.5: Декомпозиција по FD8 (review_id → review_*)

Review(review_id, review_rating, review_comment, review_date, enrollment_id)
   PK: review_id

Universal_Relation_Shifter_Base_13 = Universal_Relation_Shifter_Base_12 - {review_id, review_rating, review_comment, review_date}

Universal_Relation_Shifter_Base_13(
account_id, account_email, account_password_hash, account_name,
language_id, language_value,
course_version_id, version_number, version_created_at, version_is_active,
enrollment_id,
course_content_id, content_position,
course_lecture_id, lecture_duration_minutes, lecture_position, lecture_content_type,
tag_id, tag_type
)

Проверка за lossless join: Може да се реконструира преку enrollment_id Проверка за dependency preservation: FD8 е зачувана во Review

Чекор 6.6: Декомпозиција по FD9 (tag_id → tag_*)

Tag(tag_id, tag_type)
   PK: tag_id

Universal_Relation_Shifter_Base_14 = Universal_Relation_Shifter_Base_13 - {tag_type}

Universal_Relation_Shifter_Base_14(
account_id, account_email, account_password_hash, account_name,
language_id, language_value,
course_version_id, version_number, version_created_at, version_is_active,
enrollment_id,
course_content_id, content_position,
course_lecture_id, lecture_duration_minutes, lecture_position, lecture_content_type,
tag_id
)

Проверка за lossless join: Може да се реконструира преку tag_id Проверка за dependency preservation: FD9 е зачувана во Tag

Чекор 6.7: Декомпозиција по FD11 (course_version_id → course_version_*)

Course_Version(course_version_id, version_number, version_created_at, version_is_active, course_id)
   PK: course_version_id

Universal_Relation_Shifter_Base_15 = Universal_Relation_Shifter_Base_14 - {course_version_id, version_number, version_created_at, version_is_active}

Universal_Relation_Shifter_Base_15(
account_id, account_email, account_password_hash, account_name,
language_id, language_value,
enrollment_id,
course_content_id, content_position,
course_lecture_id, lecture_duration_minutes, lecture_position, lecture_content_type,
tag_id
)

Проверка за lossless join: Може да се реконструира преку enrollment_id Проверка за dependency preservation: FD11 и FD20 се зачувани во Course_Version

Чекор 6.8: Декомпозиција по FD13 (course_content_id → course_content_*)

Course_Content(course_content_id, content_position, course_version_id)
   PK: course_content_id

Universal_Relation_Shifter_Base_16 = Universal_Relation_Shifter_Base_15 - {course_content_id, content_position}

Universal_Relation_Shifter_Base_16(
account_id, account_email, account_password_hash, account_name,
language_id, language_value,
enrollment_id,
course_lecture_id, lecture_duration_minutes, lecture_position, lecture_content_type,
tag_id
)

Проверка за lossless join: Може да се реконструира преку enrollment_id → course_version_id Проверка за dependency preservation: FD13 и FD26 се зачувани во Course_Content

Чекор 6.9: Декомпозиција по FD15 (course_lecture_id → course_lecture_*)

Course_Lecture(course_lecture_id, lecture_duration_minutes, lecture_position, lecture_content_type, course_content_id)
   PK: course_lecture_id

Universal_Relation_Shifter_Base_17 = Universal_Relation_Shifter_Base_16 - {course_lecture_id, lecture_duration_minutes, lecture_position, lecture_content_type}

Universal_Relation_Shifter_Base_17(
account_id, account_email, account_password_hash, account_name,
language_id, language_value,
enrollment_id,
tag_id
)

Проверка за lossless join: Може да се реконструира преку enrollment_id → course_version_id → course_content_id Проверка за dependency preservation: FD15 и FD27 се зачувани во Course_Lecture

Чекор 6.10: Декомпозиција по FD19 (account_id → account_*)

Account(account_id, account_email, account_password_hash, account_name)
   PK: account_id

Universal_Relation_Shifter_Base_18 = Universal_Relation_Shifter_Base_17 - {account_id, account_email, account_password_hash, account_name}

Universal_Relation_Shifter_Base_18(
language_id, language_value,
enrollment_id,
tag_id
)

Проверка за lossless join: Може да се реконструира преку account_id во User и Expert Проверка за dependency preservation: FD19 и FD30 се зачувани во Account

Чекор 6.11: Декомпозиција по FD31 (language_id → language_value)

Language(language_id, language_value)
   PK: language_id

Universal_Relation_Shifter_Base_19 = Universal_Relation_Shifter_Base_18 - {language_id, language_value}

Universal_Relation_Shifter_Base_19(
enrollment_id,
tag_id
)

Проверка за lossless join: Може да се реконструира преку language_id во релациите за преводи Проверка за dependency preservation: FD31 и FD32 се зачувани во Language

Анализа на Universal_Relation_Shifter_Base_19

Universal_Relation_Shifter_Base_19(
enrollment_id,
tag_id
)

Universal_Relation_Shifter_Base_19 претставува колекција од foreign keys кои треба да се организираат во посебни many-to-many junction tables.

Прашање: Дали оваа релација има семантичка смисла? Одговор: НЕ! Universal_Relation_Shifter_Base_19 е само технички резултат од декомпозициите и не претставува ниеден реален бизнис концепт.

Заклучок од 3NF: Сите функционални зависности (FD1-FD33) се организирани во релации кои се во 3NF. Universal_Relation_Shifter_Base_19 повеќе не постои како валидна релација.

Останати врски (many-to-many): Преостанатите foreign keys, заедно со експертот и курсот извлечени во претходните чекори, укажуваат на many-to-many релации кои НЕ се дефинирани преку функционални зависности, туку преку бизнис логика:

  • User_Tag(user_id, tag_id) - корисник има интереси
  • Course_Tag(course_id, tag_id) - курс има тагови
  • Expert_Course(expert_id, course_id) - експерт предава курсеви

Состојба после 3NF декомпозиција

Релации креирани преку FD декомпозиција:

1NF Декомпозиција (2 релации):

Релација Атрибути Примарен клуч
Course_Translate_What_Will_Be_Learned course_translate_id, course_what_will_be_learned (course_translate_id, course_what_will_be_learned)
User_Favorite_Course user_id, course_id (user_id, course_id)

2NF Декомпозиција (8 релации):

Релација Атрибути Примарен клуч
Verification_Token verification_token_uuid, token_created_at, token_expires_at, user_id verification_token_uuid
Meeting_Reminder meeting_reminder_id, meeting_at, meeting_scheduled_at, meeting_sent, meeting_link, user_id meeting_reminder_id
Expert expert_id, account_id expert_id
Tag_Translate tag_translate_id, tag_value, tag_id, language_id tag_translate_id
Course_Translate course_translate_id, course_title_short, course_title, course_description_short, course_description, course_description_long, course_id, language_id course_translate_id
Course_Content_Translate course_content_translate_id, content_translate_title, course_content_id, language_id course_content_translate_id
Course_Lecture_Translate course_lecture_translate_id, lecture_title, lecture_content_file_name, lecture_description, lecture_content_text, course_lecture_id, language_id course_lecture_translate_id
User_Course_Progress user_course_progress_id, progress_is_completed, progress_completed_at, enrollment_id, course_lecture_id user_course_progress_id

3NF Декомпозиција (11 релации):

Релација Атрибути Примарен клуч
Account account_id, account_email, account_password_hash, account_name account_id
User user_id, user_name, user_email, user_login_provider, user_password_hash, user_is_verified, user_is_profile_complete, user_has_used_free_consultation, user_company_size, user_work_position, user_points, account_id user_id
Language language_id, language_value language_id
Course course_id, course_image_url, course_color, course_difficulty, course_duration_minutes, course_price course_id
Course_Version course_version_id, version_number, version_created_at, version_is_active, course_id course_version_id
Enrollment enrollment_id, enrollment_status, enrollment_purchase_date, enrollment_activation_date, enrollment_completion_date, user_id, course_version_id enrollment_id
Payment payment_id, payment_amount, payment_date, payment_method, payment_status, enrollment_id payment_id
Review review_id, review_rating, review_comment, review_date, enrollment_id review_id
Tag tag_id, tag_type tag_id
Course_Content course_content_id, content_position, course_version_id course_content_id
Course_Lecture course_lecture_id, lecture_duration_minutes, lecture_position, lecture_content_type, course_content_id course_lecture_id

Дополнителни релации (не од FD декомпозиција):

Релација Атрибути Примарен клуч Извор
User_Tag user_id, tag_id (user_id, tag_id) Many-to-many (ER модел)
Course_Tag course_id, tag_id (course_id, tag_id) Many-to-many (ER модел)
Expert_Course expert_id, course_id (expert_id, course_id) Many-to-many (ER модел)

7. BCNF Декомпозиција

Проверка за BCNF

За секоја релација проверуваме: за секоја нетривијална FD X → Y, дали X е суперклуч?

Account

FD Детерминант Суперклуч?
account_id → сите account_id ✓ ДА

Account е во BCNF

Language

FD Детерминант Суперклуч?
language_id → сите language_id ✓ ДА

Language е во BCNF

Verification_Token

FD Детерминант Суперклуч?
verification_token_uuid → сите verification_token_uuid ✓ ДА

Verification_Token е во BCNF

Meeting_Reminder

FD Детерминант Суперклуч?
meeting_reminder_id → сите meeting_reminder_id ✓ ДА

Meeting_Reminder е во BCNF

Expert

FD Детерминант Суперклуч?
expert_id → сите expert_id ✓ ДА

Expert е во BCNF

Tag_Translate

FD Детерминант Суперклуч?
tag_translate_id → сите tag_translate_id ✓ ДА

Tag_Translate е во BCNF

Course_Translate

FD Детерминант Суперклуч?
course_translate_id → сите course_translate_id ✓ ДА

Course_Translate е во BCNF

Course_Content_Translate

FD Детерминант Суперклуч?
course_content_translate_id → сите course_content_translate_id ✓ ДА

Course_Content_Translate е во BCNF

Course_Lecture_Translate

FD Детерминант Суперклуч?
course_lecture_translate_id → сите course_lecture_translate_id ✓ ДА

Course_Lecture_Translate е во BCNF

User_Course_Progress

FD Детерминант Суперклуч?
user_course_progress_id → сите user_course_progress_id ✓ ДА

User_Course_Progress е во BCNF

User

FD Детерминант Суперклуч?
user_id → сите user_id ✓ ДА

User е во BCNF

Course

FD Детерминант Суперклуч?
course_id → сите course_id ✓ ДА

Course е во BCNF

Enrollment

FD Детерминант Суперклуч?
enrollment_id → сите enrollment_id ✓ ДА

Enrollment е во BCNF

Payment

FD Детерминант Суперклуч?
payment_id → сите payment_id ✓ ДА

Payment е во BCNF

Review

FD Детерминант Суперклуч?
review_id → сите review_id ✓ ДА

Review е во BCNF

Tag

FD Детерминант Суперклуч?
tag_id → сите tag_id ✓ ДА

Tag е во BCNF

Course_Version

FD Детерминант Суперклуч?
course_version_id → сите course_version_id ✓ ДА

Course_Version е во BCNF

Course_Content

FD Детерминант Суперклуч?
course_content_id → сите course_content_id ✓ ДА

Course_Content е во BCNF

Course_Lecture

FD Детерминант Суперклуч?
course_lecture_id → сите course_lecture_id ✓ ДА

Course_Lecture е во BCNF

Заклучок: Сите релации се во BCNF.


8. Финален резултат и дискусија

8.1 Финални релации

User_Favorite_Course(user_id, course_id)
  - Примарен клуч: user_id, course_id
  - Кандидат клучеви: user_id, course_id
  - Надворешни клучеви: user_id, course_id

Course_Translate_What_Will_Be_Learned(course_translate_id, course_what_will_be_learned)
  - Примарен клуч: course_translate_id, course_what_will_be_learned
  - Кандидат клучеви: course_translate_id, course_what_will_be_learned
  - Надворешни клучеви: course_translate_id

Account(account_id, account_email, account_password_hash, account_name)
  - Примарен клуч: account_id
  - Кандидат клучеви: account_id, account_email
  - Надворешни клучеви: -

User(user_id, user_name, user_email, user_login_provider, user_password_hash, user_is_verified, user_is_profile_complete, user_has_used_free_consultation, user_company_size, user_work_position, user_points, account_id)
  - Примарен клуч: user_id
  - Кандидат клучеви: user_id, user_email, account_id
  - Надворешни клучеви: account_id

Expert(expert_id, account_id)
  - Примарен клуч: expert_id
  - Кандидат клучеви: expert_id, account_id
  - Надворешни клучеви: account_id

Language(language_id, language_value)
  - Примарен клуч: language_id
  - Кандидат клучеви: language_id, language_value
  - Надворешни клучеви: -

Verification_Token(verification_token_uuid, token_created_at, token_expires_at, user_id)
  - Примарен клуч: verification_token_uuid
  - Кандидат клучеви: verification_token_uuid
  - Надворешни клучеви: user_id

Meeting_Reminder(meeting_reminder_id, meeting_at, meeting_scheduled_at, meeting_sent, meeting_link, user_id)
  - Примарен клуч: meeting_reminder_id
  - Кандидат клучеви: meeting_reminder_id, meeting_link
  - Надворешни клучеви: user_id

Enrollment(enrollment_id, enrollment_status, enrollment_purchase_date, enrollment_activation_date, enrollment_completion_date, user_id, course_version_id)
  - Примарен клуч: enrollment_id
  - Кандидат клучеви: enrollment_id, (user_id, course_version_id)
  - Надворешни клучеви: user_id, course_version_id

Payment(payment_id, payment_amount, payment_date, payment_method, payment_status, enrollment_id)
  - Примарен клуч: payment_id
  - Кандидат клучеви: payment_id, enrollment_id
  - Надворешни клучеви: enrollment_id

Review(review_id, review_rating, review_comment, review_date, enrollment_id)
  - Примарен клуч: review_id
  - Кандидат клучеви: review_id, enrollment_id
  - Надворешни клучеви: enrollment_id

Course_Version(course_version_id, version_number, version_created_at, version_is_active, course_id)
  - Примарен клуч: course_version_id
  - Кандидат клучеви: course_version_id, (course_id, version_number)
  - Надворешни клучеви: course_id

Course(course_id, course_image_url, course_color, course_difficulty, course_duration_minutes, course_price)
  - Примарен клуч: course_id
  - Кандидат клучеви: course_id
  - Надворешни клучеви: -

Course_Translate(course_translate_id, course_title_short, course_title, course_description_short, course_description, course_description_long, course_id, language_id)
  - Примарен клуч: course_translate_id
  - Кандидат клучеви: course_translate_id, (course_id, language_id)
  - Надворешни клучеви: course_id, language_id

Course_Content(course_content_id, content_position, course_version_id)
  - Примарен клуч: course_content_id
  - Кандидат клучеви: course_content_id, (course_version_id, content_position)
  - Надворешни клучеви: course_version_id

Course_Content_Translate(course_content_translate_id, content_translate_title, course_content_id, language_id)
  - Примарен клуч: course_content_translate_id
  - Кандидат клучеви: course_content_translate_id, (course_content_id, language_id)
  - Надворешни клучеви: course_content_id, language_id

Course_Lecture(course_lecture_id, lecture_duration_minutes, lecture_position, lecture_content_type, course_content_id)
  - Примарен клуч: course_lecture_id
  - Кандидат клучеви: course_lecture_id, (course_content_id, lecture_position)
  - Надворешни клучеви: course_content_id

Course_Lecture_Translate(course_lecture_translate_id, lecture_title, lecture_content_file_name, lecture_description, lecture_content_text, course_lecture_id, language_id)
  - Примарен клуч: course_lecture_translate_id
  - Кандидат клучеви: course_lecture_translate_id, (course_lecture_id, language_id)
  - Надворешни клучеви: course_lecture_id, language_id

User_Course_Progress(user_course_progress_id, progress_is_completed, progress_completed_at, enrollment_id, course_lecture_id)
  - Примарен клуч: user_course_progress_id
  - Кандидат клучеви: user_course_progress_id, (enrollment_id, course_lecture_id)
  - Надворешни клучеви: enrollment_id, course_lecture_id

Tag(tag_id, tag_type)
  - Примарен клуч: tag_id
  - Кандидат клучеви: tag_id
  - Надворешни клучеви: -

Tag_Translate(tag_translate_id, tag_value, tag_id, language_id)
  - Примарен клуч: tag_translate_id
  - Кандидат клучеви: tag_translate_id, (tag_id, language_id)
  - Надворешни клучеви: tag_id, language_id

User_Tag(user_id, tag_id)
  - Примарен клуч: user_id, tag_id
  - Кандидат клучеви: user_id, tag_id
  - Надворешни клучеви: user_id, tag_id

Course_Tag(course_id, tag_id)
  - Примарен клуч: course_id, tag_id
  - Кандидат клучеви: course_id, tag_id
  - Надворешни клучеви: course_id, tag_id

Expert_Course(expert_id, course_id)
  - Примарен клуч: expert_id, course_id
  - Кандидат клучеви: expert_id, course_id
  - Надворешни клучеви: expert_id, course_id

8.2 Дискусија на разликите од моделот во Фаза P2

Преку процесот на нормализација, тргнувајќи од една универзална релација, дојдовме до 24 релации (19 од функционални зависности + 3 many-to-many junction tables + 2 multi-value tables). Споредбата со концептуалниот модел од Фаза P2 покажува следно:

Релации кои се идентични со P2

Следниве релации се целосно идентични со оние од концептуалниот ER модел:

  1. User - идентична структура и атрибути
  2. Course - идентична структура и атрибути
  3. Course_Version - идентична структура и атрибути
  4. Enrollment - идентична структура и атрибути
  5. Payment - идентична структура и атрибути (one-to-one со Enrollment)
  6. Review - идентична структура и атрибути (one-to-one со Enrollment)
  7. Tag - идентична структура и атрибути
  8. Course_Translate - идентична структура и атрибути
  9. Course_Content - идентична структура и атрибути
  10. Course_Content_Translate - идентична структура и атрибути
  11. Course_Lecture - идентична структура и атрибути
  12. Course_Lecture_Translate - идентична структура и атрибути
  13. Tag_Translate - идентична структура и атрибути
  14. Verification_Token - идентична структура и атрибути
  15. Meeting_Reminder - идентична структура и атрибути
  16. User_Course_Progress - идентична структура и атрибути

Релации кои се разликуваат од P2

  1. Account - нова релација која не постоеше во концептуалниот модел. Атрибутите за најава (email, password_hash, name) беа дуплирани во User и Expert, па се издвоени во заеднички ентитет.
  2. Expert - се разликува од P2. Атрибутите expert_name, expert_email, expert_login_provider и expert_password_hash преминаа во Account, па Expert останува само со expert_id и надворешниот клуч account_id.
  3. Language - нова релација која не постоеше во концептуалниот модел. Јазикот беше текстуален атрибут во четирите релации за преводи, а сега е самостоен ентитет со надворешни клучеви language_id.

Many-to-Many релации

  1. Expert_Course - junction table за many-to-many релација помеѓу Expert и Course
  2. Course_Tag - junction table за many-to-many релација помеѓу Course и Tag
  3. User_Tag - junction table за many-to-many релација помеѓу User и Tag

Релација со мултивредносен атрибут

  1. Course_Translate_What_Will_Be_Learned - беше дел од Course_Translate во концептуалниот модел, но се издвои во посебна релација во 1NF декомпозицијата поради мултивредносна природа (еден превод на курс има повеќе "what will be learned" ставки).
  2. User_Favorite_Course - беше дел од User во концептуалниот модел, но се издвои во посебна релација во 1NF декомпозицијата поради мултивредносна природа (еден корисник има повеќе омилени курсеви).

Клучни наоди

  1. Процесот на нормализација го потврди најголемиот дел од дизајнот - секоја декомпозиција базирана на функционалните зависности резултираше со релација која одговара на ентитет од оригиналниот модел, освен во три случаи: Account, Language и намалениот Expert.
  1. Клучот на универзалната релација - примарниот клуч беше идентификуван како композитен клуч од 10 атрибути: {verification_token_uuid, meeting_reminder_id, expert_id, course_what_will_be_learned, user_favorite_course_ids, user_course_progress_id, course_lecture_translate_id, course_content_translate_id, course_translate_id, tag_translate_id}. Овој клуч беше идентификуван преку анализа на функционалните зависности и класификација лева/десна страна на атрибутите.
  1. Декомпозицијата е lossless - на секој чекор од процесот на нормализација, оригиналната информација може да се реконструира преку JOIN операции на заедничките атрибути (foreign keys).
  1. Сите функционални зависности се зачувани - сите 33 функционални зависности (FD1-FD33) се зачувани во соодветните релации, што значи дека ограничувањата можат да се проверуваат без потреба од скапи JOIN операции.
  1. Many-to-many релации - junction tables (Expert_Course, Course_Tag, User_Tag) не произлегуваат од функционални зависности, туку од ER моделот и бизнис логиката. Овие релации немаат свои атрибути освен composite primary key од два foreign keys.

Заклучок

Процесот на нормализација го потврди најголемиот дел од дизајнот од концептуалната фаза (P2). Ова покажува дека оригиналниот ER модел беше во голема мера креиран според принципите на добра база на податоци (минимална редундантност, јасна сепарација на концепти, правилна идентификација на ентитети и релации).

Финалниот релационен модел се разликува од моделот од Фаза P2 во три релации: Account, Language и Expert.

Note: See TracWiki for help on using the wiki.