| 62 | | Критериум: кај релации со составен PK, ниту еден нефункционален атрибут да не зависи само од дел од PK. |
| 63 | | |
| 64 | | *Проверка:* |
| 65 | | {{{ |
| 66 | | R_EventDetails(global_event_id, actor_id, location_id) |
| 67 | | FDs: (global_event_id, actor_id, location_id) → ∅ |
| 68 | | }}} |
| 69 | | |
| 70 | | Нема нефункционални атрибути што зависат од дел од составениот клуч. |
| 71 | | Кај сите останати ентитети PK е едноставен, па по дефиниција се во 2NF. |
| 72 | | |
| 73 | | Со тоа сите релации се во 2NF. |
| | 91 | Критериум: секој нефункционален атрибут мора да зависи од целиот составен клуч K_R, а не |
| | 92 | само од дел од него. |
| | 93 | |
| | 94 | Постојат масовни парцијални зависности, затоа што повеќе атрибути зависат |
| | 95 | од поединечни идентификатори (подмножества на K_R). Примери: |
| | 96 | |
| | 97 | {{{ |
| | 98 | global_event_id → {sql_date, month_year, year, fraction_date, is_root_event, |
| | 99 | event_code, goldstein_scale, num_mentions, num_sources, |
| | 100 | num_articles, avg_tone} |
| | 101 | |
| | 102 | actor_id → {actor_code, actor_name, country_code, type_code} |
| | 103 | |
| | 104 | location_id → {full_name, country_code, adm1_code, latitude, longitude, |
| | 105 | feature_id} |
| | 106 | |
| | 107 | user_id → {first_name, last_name, email, password_hash, role_id, |
| | 108 | last_login_date, register_date} |
| | 109 | |
| | 110 | role_id → {role_name} |
| | 111 | |
| | 112 | plan_id → {plan_name, max_events, max_notifications, max_predictions} |
| | 113 | |
| | 114 | subscription_id → {user_id, plan_id, start_date, end_date, status} |
| | 115 | |
| | 116 | notification_id → {user_id, event_id, notification_date, status} |
| | 117 | |
| | 118 | prediction_id → {event_id, actor_id, predicted_date, prediction_type, |
| | 119 | confidence_score} |
| | 120 | |
| | 121 | conflict_id → {actor1_id, actor2_id, risk_score, predicted_date, description} |
| | 122 | |
| | 123 | analytics_id → {actor_id, event_id, date, conflict_coefficient, mentions_count, |
| | 124 | avg_tone, description} |
| | 125 | }}} |
| | 126 | |
| | 127 | Секој блок зависи само од еден дел од составниот клуч K_R, па глобалната релација R не е во 2NF. |
| | 128 | |
| | 129 | *Решение:* разложување на R според идентификаторите, со тоа секоја група атрибути се |
| | 130 | преселува во сопствена релација каде PK е токму тој идентификатор. Ова дава: |
| | 131 | |
| | 132 | {{{ |
| | 133 | Events(global_event_id PK, sql_date, month_year, year, fraction_date, is_root_event, |
| | 134 | event_code, goldstein_scale, num_mentions, num_sources, num_articles, avg_tone) |
| | 135 | |
| | 136 | Actors(actor_id PK, actor_code, actor_name, country_code, type_code) |
| | 137 | |
| | 138 | Locations(location_id PK, full_name, country_code, adm1_code, latitude, longitude, |
| | 139 | feature_id) |
| | 140 | |
| | 141 | Users(user_id PK, first_name, last_name, email, password_hash, role_id, |
| | 142 | last_login_date, register_date) |
| | 143 | |
| | 144 | Roles(role_id PK, role_name) |
| | 145 | |
| | 146 | Plan(plan_id PK, plan_name, max_events, max_notifications, max_predictions) |
| | 147 | |
| | 148 | Subscription(subscription_id PK, user_id, plan_id, start_date, end_date, status) |
| | 149 | |
| | 150 | Notifications(notification_id PK, user_id, event_id, notification_date, status) |
| | 151 | |
| | 152 | Predictions(prediction_id PK, event_id, actor_id, predicted_date, prediction_type, |
| | 153 | confidence_score) |
| | 154 | |
| | 155 | Conflict_Risk(conflict_id PK, actor1_id, actor2_id, risk_score, predicted_date, |
| | 156 | description) |
| | 157 | |
| | 158 | Event_Details(global_event_id, actor_id, location_id, |
| | 159 | PK = (global_event_id, actor_id, location_id)) |
| | 160 | |
| | 161 | Event_Analytics(analytics_id PK, actor_id, event_id, date, |
| | 162 | conflict_coefficient, mentions_count, avg_tone, description) |
| | 163 | }}} |
| | 164 | |
| | 165 | Со ова секој нефункционален атрибут во секоја релација зависи од целиот PK на таа релација. |
| | 166 | Затоа новото множество релации е во 2NF. |
| | 167 | |
| 86 | | Разложување со проекции: |
| 87 | | {{{ |
| 88 | | Actors = π_{actor_id, actor_code, actor_name, country_code, type_code}(R) |
| 89 | | Countries = π_{country_code, country_name}(R) |
| 90 | | ActorTypes = π_{type_code, type_description}(R) |
| 91 | | |
| 92 | | Locations = π_{location_id, full_name, country_code, adm1_code, latitude, longitude, feature_id}(R) |
| 93 | | Countries = π_{country_code, country_name}(R) |
| 94 | | |
| 95 | | Subscription = π_{subscription_id, user_id, plan_id, start_date, end_date, status}(R) |
| 96 | | Notifications = π_{notification_id, user_id, event_id, notification_date, status}(R) |
| 97 | | Statuses = π_{status, status_description}(R) |
| 98 | | }}} |
| 99 | | |
| 100 | | Lossless-join услови: |
| 101 | | {{{ |
| 102 | | R = Actors ⨝ Countries ⨝ ActorTypes |
| 103 | | R = Locations ⨝ Countries |
| 104 | | R = Subscription ⨝ Statuses |
| 105 | | R = Notifications ⨝ Statuses |
| 106 | | }}} |
| 107 | | |
| 108 | | Зачувување на зависности: |
| 109 | | * `actor_id → actor_code, actor_name, country_code, type_code` во Actors |
| 110 | | * `country_code → country_name` во Countries |
| 111 | | * `type_code → type_description` во Actor Types |
| 112 | | * `location_id → full_name, country_code, adm1_code, latitude, longitude, feature_id` во Locations |
| 113 | | * `subscription_id → …, status` во Subscription |
| 114 | | * `notification_id → …, status` во Notifications |
| 115 | | * `status → status_description` во Statuses |
| 116 | | |
| 117 | | Со ова сите транзитивни зависности се елиминирани, а сите релации се lossless и ги зачувуваат функционалните зависности. |
| 118 | | Затоа глобалната релација е во 3NF. |
| | 180 | За нив дефинираме посебни релации со експлицитни PK/FK: |
| | 181 | |
| | 182 | {{{ |
| | 183 | Countries(country_code PK, country_name) |
| | 184 | ActorTypes(type_code PK, type_description) |
| | 185 | Statuses(status PK, status_description) |
| | 186 | |
| | 187 | Actors(actor_id PK, actor_code, actor_name, |
| | 188 | country_code FK→Countries(country_code), |
| | 189 | type_code FK→ActorTypes(type_code)) |
| | 190 | |
| | 191 | Locations(location_id PK, full_name, |
| | 192 | country_code FK→Countries(country_code), |
| | 193 | adm1_code, latitude, longitude, feature_id) |
| | 194 | |
| | 195 | Subscription(subscription_id PK, user_id, plan_id, start_date, end_date, |
| | 196 | status FK→Statuses(status)) |
| | 197 | |
| | 198 | Notifications(notification_id PK, user_id, event_id, notification_date, |
| | 199 | status FK→Statuses(status)) |
| | 200 | }}} |
| | 201 | |
| | 202 | Lossless-join услови (базирани на PK/FK): |
| | 203 | {{{ |
| | 204 | Actors ⨝_{Actors.country_code = Countries.country_code} Countries |
| | 205 | Actors ⨝_{Actors.type_code = ActorTypes.type_code} ActorTypes |
| | 206 | Locations ⨝_{Locations.country_code = Countries.country_code} Countries |
| | 207 | Subscription ⨝_{Subscription.status = Statuses.status} Statuses |
| | 208 | Notifications⨝_{Notifications.status = Statuses.status} Statuses |
| | 209 | }}} |
| | 210 | |
| | 211 | Објаснување: |
| | 212 | * Сите споеви се FK→PK (еден кон многу), па се lossless (не се губат и не се создаваат лажни tuples). |
| | 213 | * Сите функционални зависности се зачувани: |
| | 214 | - `actor_id → actor_code, actor_name, country_code, type_code` во Actors |
| | 215 | - `country_code → country_name` во Countries |
| | 216 | - `type_code → type_description` во ActorTypes |
| | 217 | - `location_id → full_name, country_code, adm1_code, latitude, longitude, feature_id` во Locations |
| | 218 | - `subscription_id → user_id, plan_id, start_date, end_date, status` во Subscription |
| | 219 | - `notification_id → user_id, event_id, notification_date, status` во Notifications |
| | 220 | - `status → status_description` во Statuses |
| | 221 | |
| | 222 | Со ова транзитивните зависности се елиминирани, а преку PK/FK формата споевите се lossless |
| | 223 | и зависностите се зачувани. Следствено, релациите се во 3NF. |
| | 224 | |