Changes between Initial Version and Version 1 of Normalization


Ignore:
Timestamp:
07/07/26 18:48:34 (6 hours ago)
Author:
231118
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • Normalization

    v1 v1  
     1= Phase P5: Normalization =
     2
     3== a) Initial De-normalized Relation and Functional Dependencies ==
     4
     5=== Global Attribute List (unified relation R) ===
     6
     7Сите атрибути од целиот ER модел, обединети во една единствена релација R, без дупликати имиња:
     8
     9{{{
     10R(
     11  user_id, user_email, user_name, user_picture, user_created_at,
     12  tenant_id, tenant_name, owner_email, tenant_created_at,
     13  role, membership_created_at,
     14  env_id, env_name, env_created_at,
     15  env_token_id, token, et_created_at, expires_at,
     16  save_process_history, es_created_at, es_updated_at,
     17  computer_id, computer_name, computer_user, computer_ip, computer_os,
     18  first_seen, last_seen, sysmon_available,
     19  process_id, pid, process_name, cpu_percent, memory_mb,
     20  proc_username, cmdline, proc_timestamp,
     21  history_id, cpu_usage, ram_usage, disk_usage,
     22  network_sent_mb, network_recv_mb, hist_timestamp,
     23  net_conn_id, local_address, remote_address, status,
     24  nc_process_name, nc_timestamp,
     25  alert_id, alert_type, severity, description, alert_timestamp, resolved,
     26  sysmon_event_id, event_id, event_type, message, sysmon_timestamp, details
     27)
     28}}}
     29
     30=== Canonical Cover — Global Set of Functional Dependencies ===
     31
     32{{{
     33FD1:  user_id -> user_email, user_name, user_picture, user_created_at
     34FD2:  tenant_id -> tenant_name, owner_email, tenant_created_at
     35FD3:  (user_id, tenant_id) -> role, membership_created_at
     36FD4:  env_id -> tenant_id, env_name, env_created_at
     37FD5:  env_token_id -> tenant_id, env_name, token, et_created_at, expires_at
     38FD6:  (tenant_id, env_name) -> save_process_history, es_created_at, es_updated_at
     39FD7:  computer_id -> tenant_id, env_name, computer_name, computer_user,
     40                      computer_ip, computer_os, first_seen, last_seen, sysmon_available
     41FD8:  process_id -> computer_id, pid, process_name, cpu_percent, memory_mb,
     42                     proc_username, cmdline, proc_timestamp
     43FD9:  history_id -> computer_id, cpu_usage, ram_usage, disk_usage,
     44                     network_sent_mb, network_recv_mb, hist_timestamp
     45FD10: net_conn_id -> computer_id, pid, local_address, remote_address,
     46                      status, nc_process_name, nc_timestamp
     47FD11: alert_id -> computer_id, alert_type, severity, description,
     48                   alert_timestamp, resolved
     49FD12: sysmon_event_id -> computer_id, event_id, event_type, message,
     50                          sysmon_timestamp, details
     51}}}
     52
     53Ова е канонски cover (минимален сет): секоја FD има единствен атрибут / минимален сет на левата страна, нема вишок FD-и кои можат да се изведат од другите, и нема redundant атрибути на десните страни.
     54
     55----
     56
     57== b) Candidate Keys and Primary Key Selection ==
     58
     59=== Идентификување примитивни атрибути ===
     60
     61Атрибут е '''примитивен''' ако никогаш не се појавува на десната страна (RHS) на ниту една FD. Таквите атрибути мора да бидат дел од секој кандидат клуч, бидејќи ниту една FD не може да ги "произведе" - единствениот начин да се дојде до нив е да се земат директно.
     62
     63Проверка на секој атрибут од R против сите RHS на FD1-FD12:
     64
     65{{{
     66Примитивни атрибути (никогаш на RHS):
     67  user_id, env_id, env_token_id, process_id,
     68  history_id, net_conn_id, alert_id, sysmon_event_id
     69
     70Забелешка: tenant_id и computer_id НЕ се примитивни -
     71тие се појавуваат на RHS во FD4, FD5, FD7 (изведливи атрибути).
     72}}}
     73
     74=== Пробен клуч и Attribute Closure (K+) ===
     75
     76{{{
     77K = { user_id, env_id, env_token_id, process_id,
     78      history_id, net_conn_id, alert_id, sysmon_event_id }
     79}}}
     80
     81Пресметка на closure K+ чекор по чекор:
     82
     83{{{
     84K+ (почетно) = K
     85
     86Применуваме FD1 (user_id):
     87  K+ = K+ U { user_email, user_name, user_picture, user_created_at }
     88
     89Применуваме FD4 (env_id):
     90  K+ = K+ U { tenant_id, env_name, env_created_at }
     91
     92Применуваме FD5 (env_token_id):
     93  K+ = K+ U { token, et_created_at, expires_at }
     94  (tenant_id, env_name веќе се во K+)
     95
     96Применуваме FD8 (process_id):
     97  K+ = K+ U { computer_id, pid, process_name, cpu_percent, memory_mb,
     98              proc_username, cmdline, proc_timestamp }
     99
     100Применуваме FD9 (history_id):
     101  K+ = K+ U { cpu_usage, ram_usage, disk_usage,
     102              network_sent_mb, network_recv_mb, hist_timestamp }
     103
     104Применуваме FD10 (net_conn_id):
     105  K+ = K+ U { local_address, remote_address, status,
     106              nc_process_name, nc_timestamp }
     107
     108Применуваме FD11 (alert_id):
     109  K+ = K+ U { alert_type, severity, description, alert_timestamp, resolved }
     110
     111Применуваме FD12 (sysmon_event_id):
     112  K+ = K+ U { event_id, event_type, message, sysmon_timestamp, details }
     113
     114Сега computer_id е веќе во K+ -> применуваме FD7:
     115  K+ = K+ U { computer_name, computer_user, computer_ip, computer_os,
     116              first_seen, last_seen, sysmon_available }
     117
     118Сега (tenant_id, env_name) се двете во K+ -> применуваме FD6:
     119  K+ = K+ U { save_process_history, es_created_at, es_updated_at }
     120
     121Сега (user_id, tenant_id) се двете во K+ -> применуваме FD3:
     122  K+ = K+ U { role, membership_created_at }
     123
     124------------------------------------------------------
     125K+ = сите атрибути на R  =>  K е superkey
     126------------------------------------------------------
     127}}}
     128
     129=== Минималност (доказ дека К е Candidate Key) ===
     130
     131За да биде К candidate key, а не само superkey, мора да е '''минимален''' - тргнувањето на било кој атрибут од К мора да го "скрши" closure-от (да не стигне повеќе до сите атрибути).
     132
     133Бидејќи сите 8 атрибути во К се '''примитивни''' (никогаш не се на RHS на ниту една FD), секој од нив е задолжителен - ниту еден друг атрибут во целата релација не може да го "изведе". Значи тргнувањето на кој било од нив прави closure-от веднаш да падне под целосниот сет атрибути.
     134
     135{{{
     136=> K е минимален superkey => K е Candidate Key
     137
     138Candidate Key = (user_id, env_id, env_token_id, process_id,
     139                  history_id, net_conn_id, alert_id, sysmon_event_id)
     140}}}
     141
     142=== Единственост на кандидат клучот ===
     143
     144Бидејќи сите 8 атрибути во К се примитивни (задолжителни во секој candidate key), а нивниот closure веќе покрива целосно R, не постои друга комбинација атрибути (со помалку или различни примитивни атрибути) која исто така би била минимален superkey. Следствено, ова е '''единствениот''' candidate key на релацијата R.
     145
     146{{{
     147PRIMARY KEY (R) = (user_id, env_id, env_token_id, process_id,
     148                    history_id, net_conn_id, alert_id, sysmon_event_id)
     149}}}
     150
     151'''Забелешка:''' tenant_id и computer_id намерно НЕ се дел од клучот, иако интуитивно "изгледаат" како да треба - тие се redundant атрибути (изводливи преку env_id/computer_id верижно преку FD4, FD7), и нивно вклучување во клучот би го нарушило условот за минималност.
     152
     153=== Нормална форма на почетната релација ===
     154
     155{{{
     1561NF: ДА  - сите вредности се атомски, нема повторувачки групи/низи
     157
     1582NF: НЕ  - клучот е composite (8 атрибути), но постојат атрибути кои
     159           зависат само од ДЕЛ од клучот:
     160             * user_email, user_name, ... зависат само од user_id
     161             * token, et_created_at, ... зависат само од env_token_id
     162             * process_name, cpu_percent, ... зависат само од process_id
     163             * ... (аналогно за сите останати FD-и)
     164           Ова е класична парцијална зависност -> нарушување на 2NF.
     165
     166Заклучок: почетната релација R е само во 1NF.
     167Декомпозицијата започнува со отстранување на парцијалните зависности.
     168}}}
     169
     170----
     171
     172== c) Step-by-Step Decomposition ==
     173
     174=== Чекор 1: 1NF -> 2NF (отстранување на парцијални зависности) ===
     175
     176За секоја FD чија лева страна е '''подмножество''' (proper subset) на кандидат клучот, тој дел од клучот заедно со сите атрибути кои зависи од него се издвојува во нова релација.
     177
     178'''Анализирана релација:''' R (почетна, 1NF)
     179'''FD-и кои предизвикуваат проблем:''' FD1, FD2, FD3, FD4, FD5, FD6, FD7, FD8, FD9, FD10, FD11, FD12 (сите, бидејќи левите страни се proper subsets на 8-атрибутниот клуч)
     180'''Прв старт на декомпозиција:''' FD1 (user_id -> ...), потоа редоследно и останатите
     181
     182Резултат по декомпозицијата:
     183
     184{{{
     185R1: Users(user_id, user_email, user_name, user_picture, user_created_at)
     186    FD: user_id -> сите останати
     187    Candidate key: {user_id}   PK: user_id
     188
     189R2: Tenants(tenant_id, tenant_name, owner_email, tenant_created_at)
     190    FD: tenant_id -> сите останати
     191    Candidate key: {tenant_id}   PK: tenant_id
     192
     193R3: Memberships(user_id, tenant_id, role, membership_created_at)
     194    FD: (user_id, tenant_id) -> role, membership_created_at
     195    Candidate key: {user_id, tenant_id}   PK: (user_id, tenant_id)
     196
     197R4: Environments(env_id, tenant_id, env_name, env_created_at)
     198    FD: env_id -> tenant_id, env_name, env_created_at
     199    Candidate key: {env_id}   PK: env_id
     200
     201R5: EnvTokens(env_token_id, tenant_id, env_name, token, et_created_at, expires_at)
     202    FD: env_token_id -> сите останати
     203    Candidate key: {env_token_id}   PK: env_token_id
     204
     205R6: EnvSettings(tenant_id, env_name, save_process_history, es_created_at, es_updated_at)
     206    FD: (tenant_id, env_name) -> save_process_history, es_created_at, es_updated_at
     207    Candidate key: {tenant_id, env_name}   PK: (tenant_id, env_name)
     208
     209R7: Computers(computer_id, tenant_id, env_name, computer_name, computer_user,
     210              computer_ip, computer_os, first_seen, last_seen, sysmon_available)
     211    FD: computer_id -> сите останати
     212    Candidate key: {computer_id}   PK: computer_id
     213
     214R8: Processes(process_id, computer_id, pid, process_name, cpu_percent,
     215              memory_mb, proc_username, cmdline, proc_timestamp)
     216    FD: process_id -> сите останати
     217    Candidate key: {process_id}   PK: process_id
     218
     219R9: ComputerHistory(history_id, computer_id, cpu_usage, ram_usage, disk_usage,
     220                     network_sent_mb, network_recv_mb, hist_timestamp)
     221    FD: history_id -> сите останати
     222    Candidate key: {history_id}   PK: history_id
     223
     224R10: NetworkConnections(net_conn_id, computer_id, pid, local_address,
     225                         remote_address, status, nc_process_name, nc_timestamp)
     226    FD: net_conn_id -> сите останати
     227    Candidate key: {net_conn_id}   PK: net_conn_id
     228
     229R11: SecurityAlerts(alert_id, computer_id, alert_type, severity,
     230                     description, alert_timestamp, resolved)
     231    FD: alert_id -> сите останати
     232    Candidate key: {alert_id}   PK: alert_id
     233
     234R12: SysmonEvents(sysmon_event_id, computer_id, event_id, event_type,
     235                   message, sysmon_timestamp, details)
     236    FD: sysmon_event_id -> сите останати
     237    Candidate key: {sysmon_event_id}   PK: sysmon_event_id
     238}}}
     239
     240'''FD Preservation:''' секоја од оригиналните FD1-FD12 директно се содржи во точно една од новите релации (R1<-FD1, R2<-FD2, R3<-FD3, R4<-FD4, R5<-FD5, R6<-FD6, R7<-FD7, R8<-FD8, R9<-FD9, R10<-FD10, R11<-FD11, R12<-FD12) => сите FD-и се зачувани.
     241
     242'''Lossless Join проверка''' (за секоја поделба, заедничкиот атрибут мора да е клуч на барем едната страна):
     243
     244{{{
     245R  ∩ R1  = {user_id}                 -> клуч на R1              OK
     246R  ∩ R2  = {tenant_id}               -> клуч на R2              OK
     247R  ∩ R3  = {user_id, tenant_id}      -> клуч на R3              OK
     248R  ∩ R4  = {env_id}                  -> клуч на R4              OK
     249R  ∩ R5  = {env_token_id}            -> клуч на R5              OK
     250R  ∩ R6  = {tenant_id, env_name}     -> клуч на R6              OK
     251R  ∩ R7  = {computer_id}             -> клуч на R7              OK
     252R  ∩ R8  = {process_id}              -> клуч на R8              OK
     253R  ∩ R9  = {history_id}              -> клуч на R9              OK
     254R  ∩ R10 = {net_conn_id}             -> клуч на R10             OK
     255R  ∩ R11 = {alert_id}                -> клуч на R11             OK
     256R  ∩ R12 = {sysmon_event_id}         -> клуч на R12             OK
     257}}}
     258
     259Сите поделби ја задоволуваат lossless join теоремата (заедничкиот атрибут е клуч на новодобиената релација) => декомпозицијата е lossless.
     260
     261=== Чекор 2: 2NF -> 3NF (проверка на транзитивни зависности) ===
     262
     263За секоја R1-R12, се проверува дали не-клучен атрибут зависи од друг не-клучен атрибут (наместо директно од клучот).
     264
     265{{{
     266R1  (Users):               само еден детерминант (user_id) во FD1        -> нема транзитивност
     267R2  (Tenants):              само еден детерминант (tenant_id) во FD2      -> нема транзитивност
     268R3  (Memberships):          role, membership_created_at немаат меѓусебна
     269                             зависност                                    -> нема транзитивност
     270R4  (Environments):         env_name, env_created_at зависат само од
     271                             env_id, не едно од друго                     -> нема транзитивност
     272R5  (EnvTokens):             token, expires_at зависат само од
     273                             env_token_id                                 -> нема транзитивност
     274R6  (EnvSettings):           save_process_history не зависи од друг
     275                             не-клучен атрибут                            -> нема транзитивност
     276R7  (Computers):             computer_name, computer_os, ... зависат само
     277                             од computer_id (env_name овде е FK, не
     278                             детерминира ништо друго)                     -> нема транзитивност
     279R8  (Processes):             сите атрибути зависат директно од process_id -> нема транзитивност
     280R9  (ComputerHistory):       сите атрибути зависат директно од history_id -> нема транзитивност
     281R10 (NetworkConnections):    сите атрибути зависат директно од net_conn_id -> нема транзитивност
     282R11 (SecurityAlerts):        сите атрибути зависат директно од alert_id   -> нема транзитивност
     283R12 (SysmonEvents):          сите атрибути зависат директно од
     284                             sysmon_event_id                              -> нема транзитивност
     285
     286Заклучок: сите R1-R12 се веќе во 3NF по завршување на Чекор 1.
     287}}}
     288
     289=== Чекор 3: 3NF -> BCNF ===
     290
     291За BCNF, за секоја нетривијална FD X -> Y во релацијата, X мора да е superkey.
     292
     293{{{
     294R1:  единствена FD е user_id -> ...           user_id е PK -> superkey       BCNF OK
     295R2:  единствена FD е tenant_id -> ...         tenant_id е PK -> superkey     BCNF OK
     296R3:  единствена FD е (user_id,tenant_id)->... тоа е PK -> superkey           BCNF OK
     297R4:  единствена FD е env_id -> ...            env_id е PK -> superkey        BCNF OK
     298R5:  единствена FD е env_token_id -> ...      env_token_id е PK -> superkey  BCNF OK
     299R6:  единствена FD е (tenant_id,env_name)->.. тоа е PK -> superkey           BCNF OK
     300R7:  единствена FD е computer_id -> ...       computer_id е PK -> superkey   BCNF OK
     301R8:  единствена FD е process_id -> ...        process_id е PK -> superkey    BCNF OK
     302R9:  единствена FD е history_id -> ...        history_id е PK -> superkey    BCNF OK
     303R10: единствена FD е net_conn_id -> ...       net_conn_id е PK -> superkey   BCNF OK
     304R11: единствена FD е alert_id -> ...          alert_id е PK -> superkey      BCNF OK
     305R12: единствена FD е sysmon_event_id -> ...   sysmon_event_id е PK -> superkey BCNF OK
     306
     307Заклучок: сите R1-R12 се веќе во BCNF.
     308Декомпозицијата завршува по еден единствен чекор (1NF -> 2NF), бидејќи
     309истовремено ги отстранивме сите парцијални И транзитивни зависности.
     310}}}
     311
     312----
     313
     314== d) Final Result and Discussion ==
     315
     316=== Финален нормализиран дизајн ===
     317
     318{{{
     319Users(user_id, user_email, user_name, user_picture, user_created_at)
     320Tenants(tenant_id, tenant_name, owner_email, tenant_created_at)
     321Memberships(user_id, tenant_id, role, membership_created_at)
     322Environments(env_id, tenant_id, env_name, env_created_at)
     323EnvTokens(env_token_id, tenant_id, env_name, token, et_created_at, expires_at)
     324EnvSettings(tenant_id, env_name, save_process_history, es_created_at, es_updated_at)
     325Computers(computer_id, tenant_id, env_name, computer_name, computer_user,
     326          computer_ip, computer_os, first_seen, last_seen, sysmon_available)
     327Processes(process_id, computer_id, pid, process_name, cpu_percent, memory_mb,
     328          proc_username, cmdline, proc_timestamp)
     329ComputerHistory(history_id, computer_id, cpu_usage, ram_usage, disk_usage,
     330          network_sent_mb, network_recv_mb, hist_timestamp)
     331NetworkConnections(net_conn_id, computer_id, pid, local_address, remote_address,
     332          status, nc_process_name, nc_timestamp)
     333SecurityAlerts(alert_id, computer_id, alert_type, severity, description,
     334          alert_timestamp, resolved)
     335SysmonEvents(sysmon_event_id, computer_id, event_id, event_type, message,
     336          sysmon_timestamp, details)
     337}}}
     338
     339Сите 12 релации се во '''BCNF''', со зачувани функционални зависности (FD preservation) и без губење на податоци при join (lossless join).
     340
     341=== Дискусија: споредба со дизајнот од Фаза 2 ===
     342
     343Формалната normalization постапка (стартувајќи од единствена денормализирана универзална релација и attribute closure анализа) резултира со дизајн кој е '''речиси идентичен''' со реалната имплементирана шема од Фаза 2 на проектот. Ова е очекувано и добар знак - потврдува дека физичкиот дизајн од самиот почеток бил веќе близу оптимален (3NF/BCNF), без непотребна редундантност.
     344
     345Единствената разлика е физичка, не логичка: во реалната имплементација, логичкиот ентитет `Processes` е физички распослан на три табели -
     346`computer_processes`, `computer_processes_current` и `computer_processes_history` - истата структура, но со различна намена (тековна снимка наспроти историски архив). Ова е инженерска одлука мотивирана од перформанси и стратегија за архивирање на податоци, а не грешка во нормализацијата - логички, и трите се инстанци на истата нормализирана релема `Processes(process_id, computer_id, pid, process_name, cpu_percent, memory_mb, proc_username, cmdline, proc_timestamp)`.
     347
     348'''Заклучок:''' дизајнот од Фаза 2 останува дизајнот кој ќе се користи во следните фази на проектот, бидејќи е веќе во BCNF и оваа анализа само формално го потврдува тоа.