| 283 | | * In Listings(listing_id, admin_id, animal_id, …), we have listing_id → admin_id and globally admin_id → user_id, but user attributes are stored in Users, not in Listings, so no transitive dependency remains inside Listings. |
| 284 | | * In VetClinicApplications(application_id, clinic_id, …), we have application_id → clinic_id and clinic_id determines clinic attributes, but those attributes are stored in VetClinics, not in VetClinicApplications, so no transitive dependency remains there either. |
| 285 | | |
| 286 | | === Review subtypes (User Review and Clinic Review) |
| 287 | | The ER model contains a specialization of Review into UserReview and ClinicReview. |
| 288 | | If both target_user_id and target_clinic_id are stored in Reviews, the relation mixes subtype-specific semantics and may introduce nulls or invalid combinations. |
| 289 | | So we decompose: |
| 290 | | {{{ |
| 291 | | UserReviews(review_id, target_user_id) |
| 292 | | ClinicReviews(review_id, target_clinic_id) |
| 293 | | }}} |
| 294 | | |
| 295 | | Now: |
| 296 | | * Reviews stores the common review attributes |
| 297 | | * UserReviews stores the target user of a user review |
| 298 | | * ClinicReviews stores the target clinic of a clinic review |
| | 283 | * In UserSubscription(subscription_id, user_id, plan_id, start_date, end_date, subscription_status), we have: |
| | 284 | subscription_id → plan_id |
| | 285 | plan_id → plan_name, price, duration_months, plan_description, access_type |
| | 286 | Since plan descriptive attributes are stored in SubscriptionPlan, no transitive dependency remains inside UserSubscription. |
| | 287 | |
| | 288 | * In Course(course_id, course_name, course_price, course_status, instructor_id), we have: |
| | 289 | course_id → instructor_id |
| | 290 | instructor_id → user_id |
| | 291 | The user descriptive attributes are stored in UserEntity, not in Course, so no transitive dependency remains inside Course. |
| | 292 | |
| | 293 | * In QuizAttempt(attempt_id, user_id, quiz_id, score, attempt_date), we have: |
| | 294 | attempt_id → quiz_id |
| | 295 | quiz_id → lesson_id, total_points, passing_score |
| | 296 | Quiz details are stored in Quiz, so QuizAttempt does not contain transitive dependencies. |
| | 297 | |
| | 298 | * In Certificate(certificate_id, enrollment_id, issue_date, certificate_code, certificate_status), we have: |
| | 299 | certificate_id → enrollment_id |
| | 300 | enrollment_id → user_id, course_id, enroll_date, completion_status, progress_percentage |
| | 301 | |
| | 302 | Enrollment details are stored in Enrollment, so no transitive dependency remains inside Certificate. |
| | 303 | |
| | 304 | * |
| | 305 | === User specializations (User, Administrator and Instructor) |
| | 306 | The ER model contains a specialization of UserEntity into User, Administrator and Instructor. |
| | 307 | |
| | 308 | The common user attributes are stored in UserEntity: |
| | 309 | |
| | 310 | UserEntity(user_id, first_name, last_name, email, password, role) |
| | 311 | |
| | 312 | The subtype relations store only the identifier that connects the subtype with the general user entity: |
| | 313 | {{{ |
| | 314 | Users(user_id) |
| | 315 | Administrators(admin_id, user_id) |
| | 316 | Instructors(instructor_id, user_id) |
| | 317 | }}} |
| | 318 | This avoids storing user descriptive attributes multiple times in the subtype relations. Therefore, no transitive dependency remains inside the specialization relations. |
| 301 | | * UserReviews(review_id, target_user_id) |
| 302 | | * ClinicReviews(review_id, target_clinic_id) |
| 303 | | |
| 304 | | After separating subtype-specific attributes into UserReviews and ClinicReviews, all resulting relations satisfy 3NF. |
| | 321 | After checking the relations obtained from the 2NF decomposition, no additional decomposition is required for 3NF. |
| | 322 | |
| | 323 | The resulting relations after the 3NF step are: |
| | 324 | |
| | 325 | UserEntity(user_id, first_name, last_name, email, password, role) |
| | 326 | Users(user_id) |
| | 327 | Administrators(admin_id, user_id) |
| | 328 | Instructors(instructor_id, user_id) |
| | 329 | |
| | 330 | SubscriptionPlan(plan_id, plan_name, price, duration_months, plan_description, access_type) |
| | 331 | UserSubscription(subscription_id, user_id, plan_id, start_date, end_date, subscription_status) |
| | 332 | Payment(payment_id, user_id, subscription_id, amount) |
| | 333 | |
| | 334 | SupportTicket(ticket_id, user_id, admin_id, subject, ticket_description, ticket_status, created_at) |
| | 335 | |
| | 336 | Category(category_id, category_name, category_description) |
| | 337 | Course(course_id, course_name, course_price, course_status, instructor_id) |
| | 338 | CourseCategory(course_id, category_id) |
| | 339 | |
| | 340 | Module(module_id, course_id, module_title, module_description) |
| | 341 | Lesson(lesson_id, module_id, lesson_title, material) |
| | 342 | Quiz(quiz_id, lesson_id, total_points, passing_score) |
| | 343 | QuizAttempt(attempt_id, user_id, quiz_id, score, attempt_date) |
| | 344 | |
| | 345 | Enrollment(enrollment_id, user_id, course_id, enroll_date, completion_status, progress_percentage) |
| | 346 | Certificate(certificate_id, enrollment_id, issue_date, certificate_code, certificate_status) |
| | 347 | |
| | 348 | All resulting relations satisfy 3NF because for every non-trivial functional dependency X → Y inside a relation, X is a key of that relation, or the dependent attributes are stored in a separate relation determined by their own key. |