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