Changes between Version 23 and Version 24 of P5
- Timestamp:
- 04/28/26 13:15:34 (4 days ago)
Legend:
- Unmodified
- Added
- Removed
- Modified
-
P5
v23 v24 81 81 We analyze the global relation R with candidate key: 82 82 CK = {chapter_id, genre_id, list_id, notification_id, suggestion_id, comment_id, status, content_type, suggestion_type, roles, permission_level} 83 83 84 Since CK is composite, we check R for partial dependencies: 85 84 86 chapter_id → chapter_number, chapter_name, title, chapter_content, word_count, rating, published_at, view_count, story_id, chapter_created_at, chapter_updated_at — partial dependency, violates 2NF 87 85 88 genre_id → genre_name — partial dependency, violates 2NF 89 86 90 list_id → list_name, list_content, is_public, user_id, list_created_at, list_updated_at — partial dependency, violates 2NF 91 87 92 notification_id → notification_content, is_read, type, link, notification_created_at — partial dependency, violates 2NF 93 88 94 suggestion_id → original_text, suggested_text, accepted, suggestion_created_at, applied_at, story_id — partial dependency, violates 2NF 95 89 96 comment_id → comment_content, user_id, story_id, comment_created_at, comment_updated_at — partial dependency, violates 2NF 97 90 98 R is in 1NF but NOT in 2NF. We decompose R by extracting each group of partially dependent attributes into its own relation, keeping the determinant as the primary key. The story and user attributes are resolved transitively through the decomposition of chapter_id → story_id and story_id → user_id. 99 91 100 === 2NF Decomposition 92 101 After decomposition we verify that every resulting relation satisfies 2NF: 102 93 103 USERS(user_id, username, email, user_name, surname, password, user_created_at, user_updated_at) → simple primary key user_id, so it satisfies 2NF. 104 94 105 STORY(story_id, mature_content, short_description, image, story_content, user_id, story_created_at, story_updated_at) → simple primary key story_id, so it satisfies 2NF. 106 95 107 CHAPTER(chapter_id, chapter_number, chapter_name, title, chapter_content, word_count, rating, published_at, view_count, story_id, chapter_created_at, chapter_updated_at) → composite candidate key {story_id, chapter_number} exists, but all other attributes depend on the full key, not a subset. No partial dependencies, so it satisfies 2NF. 108 96 109 GENRE(genre_id, genre_name) → simple primary key genre_id, so it satisfies 2NF. 110 97 111 READING_LIST(list_id, list_name, list_content, is_public, user_id, list_created_at, list_updated_at) → simple primary key list_id, so it satisfies 2NF. 112 98 113 NOTIFICATION(notification_id, notification_content, is_read, type, link, notification_created_at) → simple primary key notification_id, so it satisfies 2NF. 114 99 115 AI_SUGGESTION(suggestion_id, original_text, suggested_text, accepted, suggestion_created_at, applied_at, story_id) → simple primary key suggestion_id, so it satisfies 2NF. 116 100 117 COMMENT(comment_id, comment_content, user_id, story_id, comment_created_at, comment_updated_at) → simple primary key comment_id, so it satisfies 2NF. 118 101 119 STATUS(story_id, status) → There are no other attributes, so it satisfies 2NF. 120 102 121 CONTENT_TYPE(notification_id, content_type) → There are no other attributes, so it satisfies 2NF. 122 103 123 SUGGESTION_TYPE(suggestion_id, suggestion_type) → There are no other attributes, so it satisfies 2NF. 124 104 125 LIKES(user_id, story_id, like_created_at) → like_created_at depends on {user_id, story_id} together, so it satisfies 2NF. 126 105 127 COLLABORATION(user_id, story_id, collab_created_at) → collab_created_at depends on {user_id, story_id} together, so it satisfies 2NF. 128 106 129 ROLES(user_id, story_id, roles) → There are no other attributes, so it satisfies 2NF. 130 107 131 PERMISSION_LEVEL(user_id, story_id, permission_level) → There are no other attributes, so it satisfies 2NF. 132 108 133 HAS_GENRE(story_id, genre_id) → There are no other attributes, so it satisfies 2NF. 134 109 135 READING_LIST_ITEMS(list_id, story_id, added_at) → added_at depends on {list_id, story_id} together, so it satisfies 2NF. 136 110 137 NOTIFY(user_id, story_id, notification_id) → There are no other attributes, so it satisfies 2NF. 138 111 139 NEED_APPROVAL(suggestion_id, story_id, chapter_id) → There are no other attributes, so it satisfies 2NF. 140 112 141 === Lossless Join Verification 142 113 143 Step 1: Decompose R → USERS + R₁ — common attribute: user_id → primary key of USERS 144 114 145 R₁ = All attributes of R, except: {username, email, user_name, surname, password, user_created_at, user_updated_at} 115 146 Step 2: Decompose R₁ → STORY + R₂ — common attribute: story_id → primary key of STORY 147 116 148 R₂ = All attributes of R, except: {username, email, user_name, surname, password, user_created_at, user_updated_at, mature_content, short_description, image, story_content, story_created_at, story_updated_at} 117 149 Step 3: Decompose R₂ → CHAPTER + R₃ — common attribute: chapter_id → primary key of CHAPTER 150 118 151 R₃ = All attributes of R, except: {username, email, user_name, surname, password, user_created_at, user_updated_at, mature_content, short_description, image, story_content, story_created_at, story_updated_at, chapter_number, chapter_name, title, chapter_content, word_count, rating, published_at, view_count, chapter_created_at, chapter_updated_at} 119 152 Step 4: Decompose R₃ → GENRE + R₄ — common attribute: genre_id → primary key of GENRE 153 120 154 R₄ = All attributes of R, except: {username, email, user_name, surname, password, user_created_at, user_updated_at, mature_content, short_description, image, story_content, story_created_at, story_updated_at, chapter_number, chapter_name, title, chapter_content, word_count, rating, published_at, view_count, chapter_created_at, chapter_updated_at, genre_name} 121 155 Step 5: Decompose R₄ → READING_LIST + R₅ — common attribute: list_id → primary key of READING_LIST 156 122 157 R₅ = All attributes of R, except: {username, email, user_name, surname, password, user_created_at, user_updated_at, mature_content, short_description, image, story_content, story_created_at, story_updated_at, chapter_number, chapter_name, title, chapter_content, word_count, rating, published_at, view_count, chapter_created_at, chapter_updated_at, genre_name, list_name, list_content, is_public, list_created_at, list_updated_at} 123 158 Step 6: Decompose R₅ → NOTIFICATION + R₆ — common attribute: notification_id → primary key of NOTIFICATION 159 124 160 R₆ = All attributes of R, except: {username, email, user_name, surname, password, user_created_at, user_updated_at, mature_content, short_description, image, story_content, story_created_at, story_updated_at, chapter_number, chapter_name, title, chapter_content, word_count, rating, published_at, view_count, chapter_created_at, chapter_updated_at, genre_name, list_name, list_content, is_public, list_created_at, list_updated_at, notification_content, is_read, type, link, notification_created_at} 125 161 Step 7: Decompose R₆ → AI_SUGGESTION + R₇ — common attribute: suggestion_id → primary key of AI_SUGGESTION 162 126 163 R₇ = All attributes of R, except: {username, email, user_name, surname, password, user_created_at, user_updated_at, mature_content, short_description, image, story_content, story_created_at, story_updated_at, chapter_number, chapter_name, title, chapter_content, word_count, rating, published_at, view_count, chapter_created_at, chapter_updated_at, genre_name, list_name, list_content, is_public, list_created_at, list_updated_at, notification_content, is_read, type, link, notification_created_at, original_text, suggested_text, accepted, suggestion_created_at, applied_at} 127 164 Step 8: Decompose R₇ → COMMENT + R₈ — common attribute: comment_id → primary key of COMMENT 165 128 166 R₈ = All attributes of R, except: {username, email, user_name, surname, password, user_created_at, user_updated_at, mature_content, short_description, image, story_content, story_created_at, story_updated_at, chapter_number, chapter_name, title, chapter_content, word_count, rating, published_at, view_count, chapter_created_at, chapter_updated_at, genre_name, list_name, list_content, is_public, list_created_at, list_updated_at, notification_content, is_read, type, link, notification_created_at, original_text, suggested_text, accepted, suggestion_created_at, applied_at, comment_content, comment_created_at, comment_updated_at} 129 167 Step 9: Decompose R₈ → STATUS + R₉ — common attribute: story_id → primary key of STORY 168 130 169 R₉ = All attributes of R, except: {... same as R₈ ..., status} 131 170 Step 10: Decompose R₉ → CONTENT_TYPE + R₁₀ — common attribute: notification_id → primary key of NOTIFICATION 171 132 172 R₁₀ = All attributes of R, except: {... same as R₉ ..., content_type} 133 173 Step 11: Decompose R₁₀ → SUGGESTION_TYPE + R₁₁ — common attribute: suggestion_id → primary key of AI_SUGGESTION 174 134 175 R₁₁ = All attributes of R, except: {... same as R₁₀ ..., suggestion_type} 135 176 Step 12: Decompose R₁₁ → LIKES + R₁₂ — common attributes: {user_id, story_id} → primary key of LIKES 177 136 178 R₁₂ = All attributes of R, except: {... same as R₁₁ ..., like_created_at} 137 179 Step 13: Decompose R₁₂ → COLLABORATION + R₁₃ — common attributes: {user_id, story_id} → primary key of COLLABORATION 180 138 181 R₁₃ = All attributes of R, except: {... same as R₁₂ ..., collab_created_at} 139 182 Step 14: Decompose R₁₃ → ROLES + R₁₄ — common attributes: {user_id, story_id} → primary key of ROLES 183 140 184 R₁₄ = All attributes of R, except: {... same as R₁₃ ..., roles} 141 185 Step 15: Decompose R₁₄ → PERMISSION_LEVEL + R₁₅ — common attributes: {user_id, story_id} → primary key of PERMISSION_LEVEL 186 142 187 R₁₅ = All attributes of R, except: {... same as R₁₄ ..., permission_level 143 188 Step 16: Decompose R₁₅ → HAS_GENRE + R₁₆ — common attributes: {story_id, genre_id} → primary key of HAS_GENRE 189 144 190 R₁₆ = All attributes of R, except: {... same as R₁₅ ..., genre_id} 145 191 Step 17: Decompose R₁₆ → READING_LIST_ITEMS + R₁₇ — common attributes: {list_id, story_id} → primary key of READING_LIST_ITEMS 192 146 193 R₁₇ = All attributes of R, except: {... same as R₁₆ ..., added_at 147 194 Step 18: Decompose R₁₇ → NOTIFY + R₁₈ — common attributes: {user_id, story_id, notification_id} → primary key of NOTIFY 195 148 196 R₁₈ = {suggestion_id, story_id, chapter_id} — only the attributes of NEED_APPROVAL remain 149 197 Step 19: Decompose R₁₈ → NEED_APPROVAL (final relation) — common attributes: {suggestion_id, story_id, chapter_id} → primary key of NEED_APPROVAL 198 150 199 R₁₉ = ∅ — all attributes fully decomposed 200 151 201 At every step the common attributes form a primary key in the extracted relation, so the entire decomposition is lossless and no information is lost. 202 152 203 Since there are no partial dependencies in any of the resulting tables, this schema satisfies 2NF. 153 204
