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 | |