| 93 | | Because the universal relation combines several independent components of the system (subscriptions, payments, support tickets, course-category assignments, quiz attempts, and certificates), the candidate key must include one identifier from each independent component that is not functionally determined by another component.. |
| 94 | | {{{#!comment It is not correct to simply collect all identifiers, because some identifiers are functionally determined by others. For example: |
| 95 | | application_id → clinic_id |
| 96 | | listing_id → animal_id, admin_id |
| 97 | | animal_id → owner_id |
| 98 | | owner_id → client_id |
| 99 | | healthrecord_id → appointment_id, animal_id |
| 100 | | appointment_id → clinic_id, animal_id |
| 101 | | |
| 102 | | |
| 103 | | Therefore, attributes such as clinic_id, animal_id, appointment_id, admin_id, owner_id, and client_id do not need to appear explicitly in the candidate key if they can already be derived from other identifiers. |
| | 93 | Because the universal relation combines several independent components of the system (subscriptions, payments, support tickets, course-category assignments, quiz attempts, and certificates), the candidate key must include one identifier from each independent component that is not functionally determined by another component. |
| | 94 | |
| 122 | | notification_id → notification_type, notification_message, notification_created_at, is_read, user_id |
| 123 | | Add: notification_type, notification_message, notification_created_at, is_read, user_id |
| | 117 | subscription_id → plan_id, start_date, end_date, subscription_status |
| | 118 | Add: plan_id, start_date, end_date, subscription_status |
| | 119 | |
| | 120 | From FD4: |
| | 121 | plan_id → plan_name, price, duration_months, access_type |
| | 122 | Add: plan_name, price, duration_months, access_type |
| 126 | | user_id → username, email, name, surname, full_name, password_hash, user_created_at |
| 127 | | Add: username, email, name, surname, full_name, password_hash, user_created_at |
| | 125 | user_id → first_name, last_name, email, password, role |
| | 126 | Add: first_name, last_name, email, password, role |
| 130 | | application_id → app_name, app_email, app_phone, app_city, app_address, submitted_at, app_status, reviewed_at, reviewed_by, denial_reason, clinic_id |
| 131 | | Add: app_name, app_email, app_phone, app_city, app_address, submitted_at, app_status, reviewed_at, reviewed_by, denial_reason, clinic_id |
| 132 | | |
| 133 | | From FD6: |
| 134 | | clinic_id → clinic_name, clinic_email, clinic_phone, clinic_address, clinic_location, clinic_city |
| 135 | | Add: clinic_name, clinic_email, clinic_phone, clinic_address, clinic_location, clinic_city |
| 136 | | |
| 137 | | From FD9: |
| 138 | | listing_id → listing_status, listing_created_at, price, listing_description, animal_id, admin_id |
| 139 | | Add: listing_status, listing_created_at, price, listing_description, animal_id, admin_id |
| | 129 | ticket_id → admin_id, subject, description, ticket_status, created_at, user_id |
| | 130 | Add: admin_id, subject, description, ticket_status, created_at |
| | 131 | (user_id already belongs to K+) |
| 146 | | animal_id → animal_name, located_name, species, animal_type, breed, sex, date_of_birth, photo_url, owner_id |
| 147 | | Add: animal_name, located_name, species, animal_type, breed, sex, date_of_birth, photo_url, owner_id |
| 148 | | |
| 149 | | From FD4: |
| 150 | | owner_id → client_id |
| 151 | | Add: client_id |
| | 138 | category_id → category_name, category_description |
| | 139 | Add: category_name, category_description |
| | 140 | |
| | 141 | From FD9: |
| | 142 | course_id → course_name, course_price, course_status, instructor_id |
| | 143 | Add: course_name, course_price, course_status, instructor_id |
| | 149 | From FD13: |
| | 150 | attempt_id → user_id, quiz_id, score, attempt_date |
| | 151 | Add: quiz_id, score, attempt_date |
| | 152 | (user_id already belongs to K+) |
| | 153 | |
| | 154 | From FD12: |
| | 155 | quiz_id → lesson_id, total_points, passing_score |
| | 156 | Add: lesson_id, total_points, passing_score |
| | 157 | |
| 159 | | healthrecord_id → hr_type, hr_description, hr_date, animal_id, appointment_id |
| 160 | | Add: hr_type, hr_description, hr_date, appointment_id |
| 161 | | animal_id already belongs to K+ |
| | 159 | lesson_id → module_id, lesson_title, material |
| | 160 | Add: module_id, lesson_title, material |
| 164 | | appointment_id → date_time, appointment_status, notes, animal_id, clinic_id |
| 165 | | Add: date_time, appointment_status, notes |
| 166 | | animal_id and clinic_id already belong to K+ |
| 167 | | |
| 168 | | From FD12: |
| 169 | | review_id → reviewer_id, rating, comment, review_created_at, updated_at, is_deleted |
| 170 | | Add: reviewer_id, rating, comment, review_created_at, updated_at, is_deleted |
| 171 | | |
| 172 | | From FD13: |
| 173 | | review_id → target_user_id |
| 174 | | Add: target_user_id |
| | 163 | module_id → module_title, module_description, course_id |
| | 164 | Add: module_title, module_description |
| | 165 | (course_id already belongs to K+) |
| | 166 | |
| | 167 | From FD15: |
| | 168 | certificate_id → enrollment_id, issue_date, certificate_code, certificate_status |
| | 169 | Add: enrollment_id, issue_date, certificate_code, certificate_status |
| 177 | | review_id → target_clinic_id |
| 178 | | Add: target_clinic_id |
| 179 | | }}} |
| 180 | | |
| 181 | | Therefore, K+ = R, so K is a superkey. |
| 182 | | |
| 183 | | K is also minimal, because removing any attribute from K would cause the loss of one independent component of the universal relation |
| 184 | | {{{#!comment |
| 185 | | * without notification_id, notification attributes cannot be derived |
| 186 | | * without application_id, application attributes cannot be derived |
| 187 | | * without listing_id, listing attributes cannot be derived |
| 188 | | * without healthrecord_id, health record attributes cannot be derived |
| 189 | | * without review_id, review attributes cannot be derived |
| 190 | | * without saved_user_id or saved_listing_id, the FavoriteListings relationship cannot be identified |
| 191 | | }}} |
| 192 | | |
| 193 | | So K is a candidate key. |
| | 172 | enrollment_id → user_id, course_id, enroll_date, completion_status, progress_percentage |
| | 173 | Add: enroll_date, completion_status, progress_percentage |
| | 174 | (user_id and course_id already belong to K+) |
| | 175 | }}} |
| | 176 | |
| | 177 | Therefore, K+ contains all attributes of the universal relation R, so K is a superkey. |
| | 178 | |
| | 179 | K is also minimal, because removing any attribute from K would cause the loss of at least one independent component of the universal relation: |
| | 180 | - without payment_id, payment/subscription information is lost |
| | 181 | - without ticket_id, support ticket information is lost |
| | 182 | - without course_id or category_id, the course-category association is lost |
| | 183 | - without attempt_id, quiz attempt and course content assessment information is lost |
| | 184 | - without certificate_id, certificate and enrollment completion information is lost |
| | 185 | |
| | 186 | So, K is a candidate key.. |