22 | | } |
23 | | |
24 | | Идејата е од оваа „една голема“ R, преку функционални зависности (ФЗ) да стигнеме до нормализирана шема (како што реално ја имаш во базата). |
25 | | |
26 | | **Функциски зависности** |
27 | | |
28 | | User: |
29 | | {{{ |
30 | | user_id → user_name, user_surname, email, password, role; и email → user_id |
31 | | }}} (email е уникатен) |
32 | | |
33 | | Teacher/Student: |
34 | | {{{ |
35 | | teacher_id → (ref User) |
36 | | }}} |
37 | | , {{{ |
38 | | student_id → (ref User, teacher_id) |
39 | | }}} |
40 | | |
41 | | Elements: |
42 | | {{{ |
43 | | element_id → symbol, element_name, atomic_number, atomic_weight, melting_point, boiling_point, hazard_type, description_element; |
44 | | }}} |
45 | | и |
46 | | {{{ |
47 | | symbol → element_id (симбол е уникатен) |
48 | | }}} |
49 | | |
50 | | |
51 | | LabEquipment: |
52 | | {{{ |
53 | | equipment_id → equipment_name, type, description, safety_info; |
54 | | }}} |
55 | | |
56 | | {{{ |
57 | | (името е UNIQUE) equipment_name → equipment_id |
58 | | }}} |
59 | | |
60 | | |
61 | | Reaction: |
62 | | {{{ |
63 | | reaction_id → r_teacher_id, element1_id, element2_id, product, conditions; и доменски (element1_id, element2_id, conditions) → product |
64 | | }}} |
65 | | |
66 | | |
67 | | Experiment: |
68 | | {{{ |
69 | | experiment_id → e_teacher_id, reaction_fk, result, safety_warning, time_stamp |
70 | | }}} |
71 | | |
72 | | |
73 | | Лог/N:M: |
74 | | |
75 | | {{{ |
76 | | (upe_user_id, upe_experiment_id) → participation_timestamp |
77 | | (uve_user_id, uve_element_id) → viewed_at_el |
78 | | (uvl_user_id, uvl_equipment_id) → viewed_at_eq |
79 | | (eleq_experiment_id, eleq_equipment_id) → (без не-клучни атрибути) |
80 | | }}} |
81 | | |
82 | | |
83 | | 2) Кандидат-клучеви |
84 | | |
85 | | User {user_id} (алтернативен {email}); |
86 | | Elements {element_id} (алтернативен {symbol}); |
87 | | LabEquipment {equipment_id} (алтернативен {equipment_name}); |
88 | | Reaction {reaction_id} (алтернативно тројката (e1,e2,conditions)); |
89 | | Experiment {experiment_id}; |
90 | | лог табели со композитни PK: {user_id,experiment_id}, {user_id,element_id}, {user_id,equipment_id}, {experiment_id,equipment_id}. |
91 | | |
92 | | 3) 1NF → 2NF → 3NF/BCNF |
93 | | |
94 | | 1NF: сите атрибути се атомарни. |
95 | | |
96 | | 2NF: во композитните лог-табели не-клучните колони зависат од целиот клуч. |
97 | | |
98 | | 3NF/BCNF: |
99 | | User(email UNIQUE), Elements(symbol UNIQUE), LabEquipment(equipment_name UNIQUE) ⇒ детерминантите се суперклучеви. |
100 | | Reaction е 3NF; со UNIQUE над (e1,e2,conditions) станува и BCNF. |
101 | | N:M табелите се BCNF. |
102 | | |
103 | | 4) Lossless & Dependency-preserving |
104 | | |
105 | | Lossless преку PK/FK споеви (Experiment ⋈ Reaction по reaction_id, Student ⋈ User по student_id=user_id …). |
106 | | Dependency-preserving: сите ФЗ ги чуваме со PK/UNIQUE/CHECK/FK (email, symbol, equipment_name; (user_id,experiment_id); физички CHECK-ови), без глобални JOIN-ови. |
107 | | |
108 | | || Табела || PK || Важни FK || НФ статус || |
109 | | || User || user_id || — || 3NF / BCNF (формално по user_id) || |
110 | | || student || student_id || teacher_id → User || 3NF / BCNF || |
111 | | || teacher || teacher_id || — || 3NF / BCNF || |
112 | | || elements || element_id || teacher_id → User || 3NF / BCNF || |
113 | | || labequipment || equipment_id || teacher_id → User || 3NF / BCNF || |
114 | | || reaction || reaction_id || teacher_id, element1_id, element2_id || 3NF / BCNF || |
115 | | || experiment || experiment_id || teacher_id, reaction_id || 3NF / BCNF || |
116 | | || experimentlabequipment || (experiment_id, equipment_id) || експеримент / опрема || 3NF / BCNF || |
117 | | || userparticipatesinexperiment|| (user_id, experiment_id) || корисник / експеримент || 3NF / BCNF || |
118 | | || userviewselement || (user_id, element_id) || корисник / елемент || 3NF / BCNF || |
119 | | || userviewslabequipment || (user_id, equipment_id) || корисник / опрема || 3NF / BCNF || |
120 | | |
121 | | **Главен клуч за целата мега-релација R** |
122 | | |
123 | | Во мега-релација се измешани повеќе домени (корисници, елементи, опрема, реакции, експерименти и логови). За да има една композитна множина што „ги покрива“ сите атрибути во R, земаме детерминанти кои ги активираат сите делови од R: |
124 | | |
125 | | GK = { user_id, reaction_id, experiment_id, element_id, equipment_id } |
126 | | |
127 | | |
128 | | |
129 | | |
130 | | Зошто ова работи: |
131 | | |
132 | | Од reaction_id добиваме: element1_id, element2_id, product, conditions, r_teacher_id. |
133 | | |
134 | | Од experiment_id добиваме: reaction_fk (→ ист како reaction_id на реакцијата), result, safety_warning, time_stamp, e_teacher_id. |
135 | | |
136 | | Од user_id + experiment_id одредуваме participation_timestamp (UPE). |
137 | | |
138 | | Од user_id + element_id одредуваме viewed_at_el (UVE). |
139 | | |
140 | | Од user_id + equipment_id одредуваме viewed_at_eq (UVL). |
141 | | |
142 | | Од experiment_id + equipment_id ја фиксираме N:M табелата ExperimentLabEquipment. |
143 | | |
144 | | Од element_id ги добиваме сите својства на елементот (+ кој наставник го креирал). |
145 | | |
146 | | Од equipment_id ги добиваме сите својства на опремата (+ кој наставник ја внел). |
147 | | |
148 | | Од user_id ги добиваме податоците за корисникот (име/улога/мејл/лозинка-хеш). |
149 | | |
150 | | Со ова, GK⁺ го опфаќа целиот атрибутски простор на R. Oд оваа множина на клучеви можеме логички да стигнеме до сите атрибути во R. |
151 | | |
152 | | |
153 | | |
154 | | **Зошто овие функционалности постојат и како се штитат со модел/ограничувања** |
155 | | |
156 | | 1. **Најава/улоги** – безбедност и права |
157 | | |
158 | | Зошто: наставник ≠ студент (внес vs. учество). |
159 | | |
160 | | Како: User.role, middleware require_login(role), email UNIQUE, хеш-пасворд. |
161 | | |
162 | | 2. **Студент → наставник** – менторирање и филтрирање извештаи |
163 | | |
164 | | Зошто: наставник следи „свои“ студенти. |
165 | | |
166 | | Како: Student(student_id, teacher_id); извештаи филтрираат по teacher_id. |
167 | | |
168 | | 3. **Eлементи/опрема** – единствена вистина и безбедност |
169 | | |
170 | | Зошто: реакции и експерименти се градат врз точни податоци; безбедносни белешки. |
171 | | |
172 | | Како: symbol UNIQUE, equipment_name UNIQUE, физички CHECK-ови. |
173 | | |
174 | | 4. **Реакции – научна конзистентност** |
175 | | |
176 | | Зошто: за исти елементи и исти услови → ист производ. |
177 | | |
178 | | Како: UNIQUE(element1_id,element2_id,COALESCE(conditions,'')), CHECK(element1_id<>element2_id). |
179 | | |
180 | | 5. **Експерименти – педагошка трага и репродуцибилност** |
181 | | |
182 | | Зошто: резултат, време, безбедност; логика за „последен експеримент по реакција“. |
183 | | |
184 | | Како: FK кон Reaction, тригер за safety_warning, индекс (reaction_id, time_stamp DESC). |
185 | | |
186 | | 6. **Учество/Прегледи (логови)** – оцена, активност, анализа |
187 | | |
188 | | Зошто: кој учествувал, што читале студентите (безбедносна документација). |
189 | | |
190 | | Како: композитни PK во лог-табелите; индекси по user_id и време. |
191 | | |
192 | | 7. **Опрема по експеримент (N:M)** – точна документација на ресурси |
193 | | |
194 | | Зошто: репродуцибилност и планирање ресурси. |
195 | | |
196 | | Како: ExperimentLabEquipment со композитен PK; batch ON CONFLICT DO NOTHING. |
197 | | |
198 | | 8. **Stored function / транзакција** – атомичност |
199 | | |
200 | | Зошто: Reaction+Experiment+(N:M) да се креираат заедно или ништо. |
201 | | |
202 | | Како: create_reaction_and_experiment_fn(...) + Python fallback транзакција. |
| 25 | |
| 26 | } |
| 27 | |
| 28 | |
| 29 | **2) Функционални зависности (логички од доменот)** |
| 30 | |
| 31 | User |
| 32 | (U1) user_id → user_name, user_surname, email, password, role |
| 33 | (U2) email → user_id (email е уникатен) |
| 34 | |
| 35 | Student / Teacher |
| 36 | (S1) student_id → teacher_id |
| 37 | |
| 38 | Elements |
| 39 | (E1) element_id → symbol, element_name, atomic_number, atomic_weight, melting_point, boiling_point, hazard_type, description_element |
| 40 | (E2) symbol → element_id (симбол е уникатен) |
| 41 | |
| 42 | LabEquipment |
| 43 | (L1) equipment_id → equipment_name, type, description, safety_info |
| 44 | (L2) equipment_name → equipment_id (име е уникатно) |
| 45 | |
| 46 | Reaction |
| 47 | (R1) reaction_id → r_teacher_id, element1_id, element2_id, product, conditions |
| 48 | (R2) (element1_id, element2_id, COALESCE(conditions,'')) → product |
| 49 | Доменски: element1_id ≠ element2_id |
| 50 | |
| 51 | Experiment |
| 52 | (X1) experiment_id → e_teacher_id, reaction_fk, result, safety_warning, time_stamp |
| 53 | |
| 54 | Лог / N:M |
| 55 | (UPE) (upe_user_id, upe_experiment_id) → participation_timestamp |
| 56 | (UVE) (uve_user_id, uve_element_id) → viewed_at_el |
| 57 | (UVL) (uvl_user_id, uvl_equipment_id) → viewed_at_eq |
| 58 | (ELEQ) (eleq_experiment_id, eleq_equipment_id) → (нема не-клунични) |
| 59 | |
| 60 | **3) Категоризација на атрибути (лево / и лево-и-десно / десно)** |
| 61 | |
| 62 | Само лево (детерминатори): |
| 63 | student_id, reaction_id, experiment_id, upe_user_id, upe_experiment_id, uve_user_id, uve_element_id, uvl_user_id, uvl_equipment_id, eleq_experiment_id, eleq_equipment_id |
| 64 | |
| 65 | И лево и десно: |
| 66 | user_id (е десно во U2), email; element_id (е десно во E2), symbol; |
| 67 | equipment_id (е десно во L2), equipment_name; element1_id, element2_id, conditions |
| 68 | |
| 69 | Само десно: |
| 70 | user_name, user_surname, password, role, teacher_id, element_name, atomic_number, atomic_weight, melting_point, boiling_point, hazard_type, description_element, type, description, safety_info, r_teacher_id, product, e_teacher_id, reaction_fk, result, safety_warning, time_stamp, participation_timestamp, viewed_at_el, viewed_at_eq |
| 71 | |
| 72 | **4) Покривачи (closures) на клучните детерминатори** |
| 73 | |
| 74 | (По дефиниција X⁺ го содржи и X.) |
| 75 | |
| 76 | user_id⁺ = { user_id, user_name, user_surname, email, password, role } |
| 77 | |
| 78 | email⁺ = { email, user_id } ∪ user_id⁺ |
| 79 | |
| 80 | student_id⁺ = { student_id, teacher_id } |
| 81 | |
| 82 | element_id⁺ = { element_id, symbol, element_name, atomic_number, atomic_weight, melting_point, boiling_point, hazard_type, description_element } |
| 83 | |
| 84 | symbol⁺ = { symbol, element_id } ∪ element_id⁺ |
| 85 | |
| 86 | equipment_id⁺ = { equipment_id, equipment_name, type, description, safety_info } |
| 87 | |
| 88 | equipment_name⁺ = { equipment_name, equipment_id } ∪ equipment_id⁺ |
| 89 | |
| 90 | reaction_id⁺ = { reaction_id, r_teacher_id, element1_id, element2_id, product, conditions } (+ својствата на e1/e2 преку E1/E2) |
| 91 | |
| 92 | (element1_id, element2_id, COALESCE(conditions,''))⁺ = { element1_id, element2_id, conditions, product } |
| 93 | |
| 94 | experiment_id⁺ = { experiment_id, e_teacher_id, reaction_fk, result, safety_warning, time_stamp } (+ целата реакција преку reaction_fk ≡ reaction_id → R1 → E1/E2) |
| 95 | |
| 96 | (upe_user_id, upe_experiment_id)⁺ = { upe_user_id, upe_experiment_id, participation_timestamp } |
| 97 | |
| 98 | (uve_user_id, uve_element_id)⁺ = { uve_user_id, uve_element_id, viewed_at_el } |
| 99 | |
| 100 | (uvl_user_id, uvl_equipment_id)⁺ = { uvl_user_id, uvl_equipment_id, viewed_at_eq } |
| 101 | |
| 102 | (eleq_experiment_id, eleq_equipment_id)⁺ = { eleq_experiment_id, eleq_equipment_id } |
| 103 | |
| 104 | **5) Важен супер-клуч за целата R** |
| 105 | |
| 106 | GK = { email, symbol, reaction_id, experiment_id, equipment_id, student_id } |
| 107 | |
| 108 | Интуиција: |
| 109 | email⁺ → цел USER (и user_id), symbol⁺ → конкретен ELEMENT, |
| 110 | reaction_id⁺ → (e1,e2,conditions,product) (+ e1/e2 својства), |
| 111 | experiment_id⁺ → резултат/време/безбедност + врска до реакција, |
| 112 | equipment_id⁺ → LAB_EQUIPMENT, student_id⁺ → ментор. |
| 113 | Оттука, добиваме и UPE/UVE/UVL/ELEQ зависностите (учества/прегледи/N:M). |
| 114 | |
| 115 | **6) 1НФ → 2НФ → 3НФ/BCNF** |
| 116 | |
| 117 | 6.1) Прва нормална форма (1НФ) |
| 118 | |
| 119 | Во моделот сите атрибути се атомарни (нема листи/повторувачки групи). |
| 120 | |
| 121 | Значи, R е во 1НФ, но не е во 2НФ: огромен број атрибути зависат само од делови на (имплицитниот) композитен супер-клуч на R. |
| 122 | |
| 123 | |
| 124 | 6.2) Втора нормална форма (2НФ) — отстранување парцијални зависности |
| 125 | |
| 126 | Сите атрибути што зависат од дел од композитниот супер-клуч на R, ги вадиме во посебни релации каде што тој дел е клуч. Практично ова се „очигледните“ ентитети/врски од доменот. |
| 127 | |
| 128 | Декомпозиција (lossless) по проблематични ФЗ: |
| 129 | |
| 130 | D1 – USER (U1/U2): |
| 131 | R → R¹_user(user_id, user_name, user_surname, email, password, role) + остаток |
| 132 | (овде email е алтернативен клуч). |
| 133 | Ефект: сите атрибути на корисникот веќе зависат од цел клуч на релацијата → 2НФ (и повеќе). |
| 134 | |
| 135 | D2 – ELEMENT (E1/E2): |
| 136 | R → R²_element(element_id, symbol, element_name, atomic_number, atomic_weight, melting_point, boiling_point, hazard_type, description_element) + остаток. |
| 137 | Ефект: нема атрибут што зависи само од дел од клуч → 2НФ. |
| 138 | |
| 139 | D3 – LAB_EQUIPMENT (L1/L2): |
| 140 | R → R³_equipment(equipment_id, equipment_name, type, description, safety_info) + остаток. |
| 141 | |
| 142 | D4 – REACTION (R1/R2): |
| 143 | R → R⁴_reaction(reaction_id, r_teacher_id, element1_id, element2_id, conditions, product) + остаток. |
| 144 | Напомена: за 3НФ/BCNF ќе се вратиме на (R2). |
| 145 | |
| 146 | D5 – EXPERIMENT (X1): |
| 147 | R → R⁵_experiment(experiment_id, e_teacher_id, reaction_id, result, safety_warning, time_stamp) + остаток. |
| 148 | |
| 149 | D6 – STUDENT (S1) и TEACHER: |
| 150 | R → R⁶_student(student_id, teacher_id); TEACHER го третираме како под-склоп на USER или експлицитно R⁶_teacher(teacher_id). |
| 151 | |
| 152 | D7 – Логови и N:M (UPE/UVE/UVL/ELEQ): |
| 153 | R → R⁷_upe(user_id, experiment_id, participation_timestamp) – PK(user_id,experiment_id) |
| 154 | R → R⁸_uve(user_id, element_id, viewed_at_el) – PK(user_id,element_id) |
| 155 | R → R⁹_uvl(user_id, equipment_id, viewed_at_eq) – PK(user_id,equipment_id) |
| 156 | R → R¹⁰_eleq(experiment_id, equipment_id) – PK(experiment_id,equipment_id) |
| 157 | |
| 158 | |
| 159 | 6.3) Трета нормална форма (3НФ) и BCNF |
| 160 | |
| 161 | Сега проверуваме дали останале: |
| 162 | |
| 163 | Транзитивни зависности (за 3НФ), и/или |
| 164 | |
| 165 | ФЗ чии детерминатори не се суперклучеви (за BCNF). |
| 166 | |
| 167 | Проверки по релација: |
| 168 | |
| 169 | USER, ELEMENT, LAB_EQUIPMENT, EXPERIMENT, STUDENT, UPE/UVE/UVL/ELEQ |
| 170 | – секоја има јасен клуч; нема транзитивни зависности во рамка на релацијата → 3НФ. |
| 171 | – воедно, детерминаторите се клучеви/алтернативни клучеви → BCNF. |
| 172 | |
| 173 | REACTION – единствениот ризик: |
| 174 | ФЗ (R2) вели: (element1_id, element2_id, COALESCE(conditions,'')) → product. |
| 175 | Ако тројката не е суперклуч, тогаш (R2) би ја кршела 3НФ/BCNF (бидејќи product не е прим-атрибут). |
| 176 | Решение: ставаме |
| 177 | UNIQUE(element1_id, element2_id, COALESCE(conditions,'')) |
| 178 | → тројката станува суперклуч → (R2) е добра дури и за BCNF. |
| 179 | |
| 180 | Заклучок: по оваа корекција, сите релации се во BCNF. |
| 181 | |
| 182 | 7) Финален список на релации (во 3НФ / BCNF) |
| 183 | |
| 184 | USER(user_id PK, user_name, user_surname, email UNIQUE, password, role) |
| 185 | |
| 186 | TEACHER(teacher_id PK → USER(user_id)) |
| 187 | |
| 188 | STUDENT(student_id PK, teacher_id FK → TEACHER(teacher_id)) |
| 189 | |
| 190 | ELEMENT(element_id PK, symbol UNIQUE, element_name, atomic_number, atomic_weight, |
| 191 | melting_point, boiling_point, hazard_type, description_element) |
| 192 | |
| 193 | LAB_EQUIPMENT(equipment_id PK, equipment_name UNIQUE, type, description, safety_info) |
| 194 | |
| 195 | REACTION(reaction_id PK, r_teacher_id FK → TEACHER, |
| 196 | element1_id FK → ELEMENT, element2_id FK → ELEMENT, |
| 197 | conditions, product, |
| 198 | CHECK(element1_id <> element2_id), |
| 199 | UNIQUE(element1_id, element2_id, COALESCE(conditions,''))) ← избраната тројка |
| 200 | |
| 201 | EXPERIMENT(experiment_id PK, e_teacher_id FK → TEACHER, |
| 202 | reaction_id FK → REACTION(reaction_id), |
| 203 | result, safety_warning, time_stamp DEFAULT now()) |
| 204 | |
| 205 | UPE(user_id FK → USER, experiment_id FK → EXPERIMENT, |
| 206 | participation_timestamp DEFAULT now(), PK(user_id,experiment_id)) |
| 207 | |
| 208 | UVE(user_id FK → USER, element_id FK → ELEMENT, |
| 209 | viewed_at_el DEFAULT now(), PK(user_id,element_id)) |
| 210 | |
| 211 | UVL(user_id FK → USER, equipment_id FK → LAB_EQUIPMENT, |
| 212 | viewed_at_eq DEFAULT now(), PK(user_id,equipment_id)) |
| 213 | |
| 214 | ELEQ(experiment_id FK → EXPERIMENT, equipment_id FK → LAB_EQUIPMENT, |
| 215 | PK(experiment_id,equipment_id)) |
| 216 | |