Changes between Version 2 and Version 3 of Normalization


Ignore:
Timestamp:
08/23/26 20:05:22 (4 days ago)
Author:
231118
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • Normalization

    v2 v3  
    55=== Global Attribute List (unified relation R) ===
    66
    7 Сите атрибути од целиот ER модел, обединети во една единствена релација R, без дупликати имиња:
     7Сите атрибути од целиот (v04) ER модел, обединети во една единствена релација R, со глобално единствени имиња:
    88
    99{{{
     
    1414  env_id, env_name, env_created_at,
    1515  env_token_id, token, et_created_at, expires_at,
    16   save_process_history, es_created_at, es_updated_at,
     16  save_process_history, save_metrics_history, save_network_history,
     17  es_created_at, es_updated_at,
     18  network_id, network_name, cidr, gateway_ip, vlan,
     19  group_id, group_name, group_description, parent_group_id,
    1720  computer_id, computer_name, computer_user, computer_ip, computer_os,
    1821  first_seen, last_seen, sysmon_available,
     22  device_id, device_name, device_type, device_ip, mac, device_status,
    1923  process_id, pid, process_name, cpu_percent, memory_mb,
    2024  proc_username, cmdline, proc_timestamp,
    2125  history_id, cpu_usage, ram_usage, disk_usage,
    2226  network_sent_mb, network_recv_mb, hist_timestamp,
    23   net_conn_id, local_address, remote_address, status,
     27  net_conn_id, nc_pid, local_address, remote_address, conn_status,
    2428  nc_process_name, nc_timestamp,
     29  service_id, service_name, port, protocol, service_status, last_checked,
     30  availability_id, is_available, response_time_ms, sa_timestamp,
    2531  alert_id, alert_type, severity, description, alert_timestamp, resolved,
    2632  sysmon_event_id, event_id, event_type, message, sysmon_timestamp, details
     
    3642FD4:  env_id -> tenant_id, env_name, env_created_at
    3743FD5:  env_token_id -> tenant_id, env_name, token, et_created_at, expires_at
    38 FD6:  (tenant_id, env_name) -> save_process_history, es_created_at, es_updated_at
    39 FD7:  computer_id -> tenant_id, env_name, computer_name, computer_user,
    40                       computer_ip, computer_os, first_seen, last_seen, sysmon_available
    41 FD8:  process_id -> computer_id, pid, process_name, cpu_percent, memory_mb,
     44FD6:  (tenant_id, env_name) -> save_process_history, save_metrics_history,
     45                                save_network_history, es_created_at, es_updated_at
     46FD7:  network_id -> tenant_id, env_name, network_name, cidr, gateway_ip, vlan
     47FD8:  group_id -> tenant_id, group_name, group_description, parent_group_id
     48FD9:  computer_id -> tenant_id, env_name, computer_name, computer_user,
     49                      computer_ip, computer_os, first_seen, last_seen,
     50                      sysmon_available, network_id, group_id
     51FD10: device_id -> network_id, device_name, device_type, device_ip, mac, device_status
     52FD11: process_id -> computer_id, pid, process_name, cpu_percent, memory_mb,
    4253                      proc_username, cmdline, proc_timestamp
    43 FD9: history_id -> computer_id, cpu_usage, ram_usage, disk_usage,
     54FD12: history_id -> computer_id, cpu_usage, ram_usage, disk_usage,
    4455                      network_sent_mb, network_recv_mb, hist_timestamp
    45 FD10: net_conn_id -> computer_id, pid, local_address, remote_address,
    46                       status, nc_process_name, nc_timestamp
    47 FD11: alert_id -> computer_id, alert_type, severity, description,
     56FD13: net_conn_id -> computer_id, nc_pid, local_address, remote_address,
     57                      conn_status, nc_process_name, nc_timestamp
     58FD14: service_id -> computer_id, service_name, port, protocol,
     59                     service_status, last_checked
     60FD15: availability_id -> service_id, is_available, response_time_ms, sa_timestamp
     61FD16: alert_id -> computer_id, alert_type, severity, description,
    4862                   alert_timestamp, resolved
    49 FD12: sysmon_event_id -> computer_id, event_id, event_type, message,
     63FD17: sysmon_event_id -> computer_id, event_id, event_type, message,
    5064                          sysmon_timestamp, details
    5165}}}
    5266
    53 Ова е канонски cover (минимален сет): секоја FD има единствен атрибут / минимален сет на левата страна, нема вишок FD-и кои можат да се изведат од другите, и нема redundant атрибути на десните страни.
     67Дополнителна (алтернативна) зависност: (computer_id, port, protocol) -> service_id
     68(единственост на сервис по компјутер/порта/протокол — алтернативен клуч на NetworkServices).
     69
     70Ова е канонски cover (минимален сет): секоја FD има минимален детерминант на левата страна, нема вишок FD-и што можат да се изведат од другите, и нема redundant атрибути на десните страни.
    5471
    5572----
     
    5976=== Идентификување примитивни атрибути ===
    6077
    61 Атрибут е примитивен ако никогаш не се појавува на десната страна (RHS) на ниту една FD. Таквите атрибути мора да бидат дел од секој кандидат клуч, бидејќи ниту една FD не може да ги "произведе" - единствениот начин да се дојде до нив е да се земат директно.
    62 
    63 Проверка на секој атрибут од R против сите RHS на FD1-FD12:
     78Атрибут е примитивен ако никогаш не се појавува на десната страна (RHS) на ниту една FD. Таквите атрибути мора да бидат дел од секој кандидат клуч.
     79
     80Проверка на секој сурогат-идентификатор против сите RHS на FD1-FD17:
    6481
    6582{{{
    6683Примитивни атрибути (никогаш на 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 (изведливи атрибути).
     84  user_id, env_id, env_token_id, device_id,
     85  process_id, history_id, net_conn_id, availability_id,
     86  alert_id, sysmon_event_id
     87
     88НЕ се примитивни (се појавуваат на RHS, значи изведливи):
     89  tenant_id     (RHS во FD4, FD5, FD7, FD8, FD9)
     90  env_name      (RHS во FD4, FD5, FD7, FD9)
     91  network_id    (RHS во FD9, FD10)
     92  group_id      (RHS во FD9)
     93  computer_id   (RHS во FD11..FD14, FD16, FD17)
     94  service_id    (RHS во FD15)
    7295}}}
    7396
     
    7598
    7699{{{
    77 K = { user_id, env_id, env_token_id, process_id,
    78       history_id, net_conn_id, alert_id, sysmon_event_id }
     100K = { user_id, env_id, env_token_id, device_id,
     101      process_id, history_id, net_conn_id, availability_id,
     102      alert_id, sysmon_event_id }
    79103}}}
    80104
     
    84108K+ (почетно) = K
    85109
    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 }
     110FD1  (user_id):        + user_email, user_name, user_picture, user_created_at
     111FD4  (env_id):         + tenant_id, env_name, env_created_at
     112FD5  (env_token_id):   + token, et_created_at, expires_at
     113FD10 (device_id):      + network_id, device_name, device_type, device_ip, mac, device_status
     114FD11 (process_id):     + computer_id, pid, process_name, cpu_percent, memory_mb,
     115                         proc_username, cmdline, proc_timestamp
     116FD12 (history_id):     + cpu_usage, ram_usage, disk_usage,
     117                         network_sent_mb, network_recv_mb, hist_timestamp
     118FD13 (net_conn_id):    + nc_pid, local_address, remote_address, conn_status,
     119                         nc_process_name, nc_timestamp
     120FD15 (availability_id):+ service_id, is_available, response_time_ms, sa_timestamp
     121FD16 (alert_id):       + alert_type, severity, description, alert_timestamp, resolved
     122FD17 (sysmon_event_id):+ event_id, event_type, message, sysmon_timestamp, details
     123
     124Сега network_id e во K+ -> FD7:
     125                       + network_name, cidr, gateway_ip, vlan
     126Сега service_id e во K+ -> FD14:
     127                       + service_name, port, protocol, service_status, last_checked
     128Сега computer_id e во K+ -> FD9:
     129                       + computer_name, computer_user, computer_ip, computer_os,
     130                         first_seen, last_seen, sysmon_available, group_id
     131Сега group_id e во K+ -> FD8:
     132                       + group_name, group_description, parent_group_id
     133Сега (tenant_id, env_name) се двете во K+ -> FD6:
     134                       + save_process_history, save_metrics_history,
     135                         save_network_history, es_created_at, es_updated_at
     136Сега (user_id, tenant_id) се двете во K+ -> FD3:
     137                       + role, membership_created_at
    123138
    124139------------------------------------------------------
     
    129144=== Минималност (доказ дека К е Candidate Key) ===
    130145
    131 За да биде К candidate key, а не само superkey, мора да е минимален - тргнувањето на било кој атрибут од К мора да го "скрши" closure-от (да не стигне повеќе до сите атрибути).
    132 
    133 Бидејќи сите 8 атрибути во К се примитивни (никогаш не се на RHS на ниту една FD), секој од нив е задолжителен - ниту еден друг атрибут во целата релација не може да го "изведе". Значи тргнувањето на кој било од нив прави closure-от веднаш да падне под целосниот сет атрибути.
     146Сите 10 атрибути во К се примитивни (никогаш не се на RHS), па секој е задолжителен - ниту еден друг атрибут не може да го изведе. Секој од нив е и единствениот детерминант на своите зависни атрибути (пр. само env_token_id го дава token/expires_at; само availability_id ги дава is_available/response_time_ms). Тргнувањето на кој било од нив прави closure-от веднаш да падне под целосниот сет.
    134147
    135148{{{
    136149=> K е минимален superkey => K е Candidate Key
    137150
    138 Candidate Key = (user_id, env_id, env_token_id, process_id,
    139                  history_id, net_conn_id, alert_id, sysmon_event_id)
     151Candidate Key = (user_id, env_id, env_token_id, device_id,
     152                 process_id, history_id, net_conn_id, availability_id,
     153                 alert_id, sysmon_event_id)
    140154}}}
    141155
    142156=== Единственост на кандидат клучот ===
    143157
    144 Бидејќи сите 8 атрибути во К се примитивни (задолжителни во секој candidate key), а нивниот closure веќе покрива целосно R, не постои друга комбинација атрибути (со помалку или различни примитивни атрибути) која исто така би била минимален superkey. Следствено, ова е единствениот candidate key на релацијата R.
    145 
    146 {{{
    147 PRIMARY 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), и нивно вклучување во клучот би го нарушило условот за минималност.
     158Бидејќи сите 10 атрибути во К се примитивни (задолжителни во секој candidate key), а нивниот closure веќе го покрива целосно R, не постои друга минимална комбинација атрибути што би била superkey. Следствено, ова е единствениот candidate key на R.
     159
     160{{{
     161PRIMARY KEY (R) = (user_id, env_id, env_token_id, device_id,
     162                   process_id, history_id, net_conn_id, availability_id,
     163                   alert_id, sysmon_event_id)
     164}}}
     165
     166Забелешка: tenant_id, env_name, network_id, group_id, computer_id и service_id
     167намерно НЕ се дел од клучот - тие се изведливи (redundant) атрибути преку
     168FD4/FD7/FD9/FD10/FD14/FD15, и нивно вклучување би ја нарушило минималноста.
    152169
    153170=== Нормална форма на почетната релација ===
    154171
    155172{{{
    156 1NF: ДА  - сите вредности се атомски, нема повторувачки групи/низи
    157 
    158 2NF: НЕ  - клучот е 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
     1731NF: ДА  - сите вредности се атомски, нема повторувачки групи/низи.
     174
     1752NF: НЕ  - клучот е составен (10 атрибути), но постојат атрибути што зависат
     176           само од ДЕЛ од клучот (парцијални зависности), пример:
     177             * user_email, ...        зависат само од user_id
     178             * token, expires_at, ... зависат само од env_token_id
     179             * device_name, mac, ...  зависат само од device_id
     180             * is_available, ...      зависат само од availability_id
    163181             * ... (аналогно за сите останати FD-и)
    164            Ова е класична парцијална зависност -> нарушување на 2NF.
    165182
    166183Заклучок: почетната релација R е само во 1NF.
    167 Декомпозицијата започнува со отстранување на парцијалните зависности.
    168184}}}
    169185
     
    174190=== Чекор 1: 1NF -> 2NF (отстранување на парцијални зависности) ===
    175191
    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 {{{
    185 R1: 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 
    189 R2: Tenants(tenant_id, tenant_name, owner_email, tenant_created_at)
    190     FD: tenant_id -> сите останати
    191     Candidate key: {tenant_id}   PK: tenant_id
    192 
    193 R3: 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 
    197 R4: 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 
    201 R5: 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 
    205 R6: 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 
    209 R7: 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 
    214 R8: 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 
    219 R9: ComputerHistory(history_id, computer_id, cpu_usage, ram_usage, disk_usage,
     192За секоја FD чија лева страна е proper subset на кандидат клучот, тој детерминант заедно со зависните атрибути се издвојува во нова релација.
     193
     194{{{
     195R1  Users(user_id, user_email, user_name, user_picture, user_created_at)
     196    PK: user_id
     197
     198R2  Tenants(tenant_id, tenant_name, owner_email, tenant_created_at)
     199    PK: tenant_id
     200
     201R3  Memberships(user_id, tenant_id, role, membership_created_at)
     202    PK: (user_id, tenant_id)
     203
     204R4  Environments(env_id, tenant_id, env_name, env_created_at)
     205    PK: env_id
     206
     207R5  EnvTokens(env_token_id, tenant_id, env_name, token, et_created_at, expires_at)
     208    PK: env_token_id
     209
     210R6  EnvSettings(tenant_id, env_name, save_process_history, save_metrics_history,
     211                save_network_history, es_created_at, es_updated_at)
     212    PK: (tenant_id, env_name)
     213
     214R7  Networks(network_id, tenant_id, env_name, network_name, cidr, gateway_ip, vlan)
     215    PK: network_id
     216
     217R8  ComputerGroups(group_id, tenant_id, group_name, group_description, parent_group_id)
     218    PK: group_id     (parent_group_id -> ComputerGroups.group_id, само-референца)
     219
     220R9  Computers(computer_id, tenant_id, env_name, computer_name, computer_user,
     221              computer_ip, computer_os, first_seen, last_seen, sysmon_available,
     222              network_id, group_id)
     223    PK: computer_id
     224
     225R10 NetworkDevices(device_id, network_id, device_name, device_type,
     226                   device_ip, mac, device_status)
     227    PK: device_id
     228
     229R11 Processes(process_id, computer_id, pid, process_name, cpu_percent, memory_mb,
     230              proc_username, cmdline, proc_timestamp)
     231    PK: process_id
     232
     233R12 ComputerHistory(history_id, computer_id, cpu_usage, ram_usage, disk_usage,
    220234                    network_sent_mb, network_recv_mb, hist_timestamp)
    221     FD: history_id -> сите останати
    222     Candidate key: {history_id}   PK: history_id
    223 
    224 R10: 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 
    229 R11: SecurityAlerts(alert_id, computer_id, alert_type, severity,
    230                      description, alert_timestamp, resolved)
    231     FD: alert_id -> siте останати
    232     Candidate key: {alert_id}   PK: alert_id
    233 
    234 R12: 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 со помош на Chasing Алгоритам ===
    243 
    244 За да докажеме дека декомпозицијата е без загуби (Lossless Join) над повеќе од две релации, конструираме матрица на бркање (Chase Matrix). Колоните ги претставуваат сите глобални атрибути, а редовите соодветствуваат на релациите R1 до R12.
    245 
    246 Ако релацијата го содржи атрибутот, во соодветната ќелија се впишува симболот a_j (каде j е индексот на колоната), во спротивно се впишува b_i,j.
    247 
    248 Применувајќи ги функционалните зависности последователно врз матрицата, ги изедначуваме b вредностите со соодветните a вредности:
    249 
    250 1. Примена на FD1 (user_id -> ...): Сите редови кои имаат a_user_id (тоа се R1 и R3) ги споделуваат и добиваат вредности за a_user_email, a_user_name, a_user_picture, a_user_created_at.
    251 2. Примена на FD4 (env_id -> ...): Бидејќи само R4 го има примитивното env_id, тоа го детерминира tenant_id и env_name во тој ред.
    252 3. Примена на FD8 (process_id -> ...): Го изедначува computer_id во сите редови каде постои соодветната релација.
    253 4. Примена на FD7 (computer_id -> ...): Кај сите редови што содржат computer_id (R7, R8, R9, R10, R11, R12), b вредностите за атрибутите на компјутерот (вклучувајќи ги tenant_id и env_name) се трансформираат во а симболи.
    254 
    255 Клучен чекор за успешност на алгоритмот: Бидејќи по извршените трансформации редовите за системските ентитети (R4, R5, R6, R7) сега сите имаат заеднички a_tenant_id и a_env_name, со примена на FD6 (tenant_id, env_name -> save_process_history, ...), овие атрибути се пропагираат низ сите нив како а симболи.
    256 
    257 На крајот од процесот на "бркање", бидејќи почетниот кандидат клуч е составен токму од овие независни примитивни клучеви чии релации се спојуваат преку нивните соодветни странски клучеви, во матрицата се генерира целосен ред составен само од а симболи.
    258 
    259 Ова математички докажува дека декомпозицијата има Lossless Join карактеристика.
    260 
    261 === Чекор 2: 2NF -> 3NF (проверка на транзитивни зависности) ===
    262 
    263 За секоја R1-R12, се проверува дали не-клучен атрибут зависe од друг не-клучен атрибут (наместо директно од клучот).
    264 
    265 {{{
    266 R1  (Users):               само еден детерминант (user_id) во FD1        -> нема транзитивност
    267 R2  (Tenants):             само еден детерминант (tenant_id) во FD2      -> нема транзитивност
    268 R3  (Memberships):         role, membership_created_at немаат меѓусебна
    269                            зависност                                    -> нема транзитивност
    270 R4  (Environments):         env_name, env_created_at зависат само од
    271                            env_id, не едно од друго                     -> нема транзитивност
    272 R5  (EnvTokens):             token, expires_at зависат само од
    273                            env_token_id                                 -> нема транзитивност
    274 R6  (EnvSettings):           save_process_history не зависи од друг
    275                            не-клучен атрибут                            -> нема транзитивност
    276 R7  (Computers):             computer_name, computer_os, ... зависат само
    277                            од computer_id (env_name овде е FK, не
    278                            детерминира ништо друго)                      -> нема транзитивност
    279 R8  (Processes):             сите атрибути зависат директно од process_id -> нема транзитивност
    280 R9  (ComputerHistory):       сите атрибути зависат директно од history_id -> нема транзитивност
    281 R10 (NetworkConnections):    сите атрибути зависат директно од net_conn_id -> нема транзитивност
    282 R11 (SecurityAlerts):        сите атрибути зависат директно од alert_id   -> нема транзитивност
    283 R12 (SysmonEvents):          сите атрибути зависат директно од
    284                            sysmon_event_id                              -> нема транзитивност
    285 
    286 Заклучок: сите R1-R12 се веќе во 3NF по завршување на Чекор 1.
     235    PK: history_id
     236
     237R13 NetworkConnections(net_conn_id, computer_id, nc_pid, local_address,
     238                       remote_address, conn_status, nc_process_name, nc_timestamp)
     239    PK: net_conn_id
     240
     241R14 NetworkServices(service_id, computer_id, service_name, port, protocol,
     242                    service_status, last_checked)
     243    PK: service_id     Alternate key: (computer_id, port, protocol)
     244
     245R15 ServiceAvailability(availability_id, service_id, is_available,
     246                        response_time_ms, sa_timestamp)
     247    PK: availability_id
     248
     249R16 SecurityAlerts(alert_id, computer_id, alert_type, severity,
     250                   description, alert_timestamp, resolved)
     251    PK: alert_id
     252
     253R17 SysmonEvents(sysmon_event_id, computer_id, event_id, event_type,
     254                 message, sysmon_timestamp, details)
     255    PK: sysmon_event_id
     256}}}
     257
     258FD Preservation: секоја од оригиналните FD1-FD17 директно се содржи во точно една
     259нова релација (R1<-FD1, ..., R17<-FD17), плус алтернативниот клуч на NetworkServices
     260се чува во R14 => сите зависности се зачувани.
     261
     262=== Формален доказ за Lossless Join (Chasing алгоритам) ===
     263
     264Конструираме матрица на бркање (Chase Matrix): колоните се сите глобални атрибути,
     265редовите се релациите R1-R17. Ако релацијата го содржи атрибутот, во ќелијата се
     266впишува a_j, инаку b_i,j. Ги применуваме FD-ите и ги изедначуваме b со a вредности:
     267
     2681. FD11/FD12/FD13/FD16/FD17 (примитивни клучеви process_id/history_id/net_conn_id/
     269   alert_id/sysmon_event_id) го изедначуваат computer_id во сите нивни редови.
     2702. FD15 (availability_id) го изедначува service_id, а FD14 потоа го носи computer_id.
     2713. FD10 (device_id) го изедначува network_id.
     2724. FD9 (computer_id -> ...) кај сите редови што содржат computer_id (R9..R17) ги
     273   претвора b вредностите за атрибутите на компјутерот, вклучувајќи tenant_id,
     274   env_name, network_id и group_id, во a симболи.
     2755. FD7 (network_id) и FD8 (group_id) ги пропагираат мрежните и организациските
     276   атрибути како a симболи.
     2776. Клучен чекор: бидејќи по трансформациите редовите R4, R5, R6, R7, R9 сега имаат
     278   заеднички a_tenant_id и a_env_name, со FD6 овие атрибути (знамињата за историја)
     279   се пропагираат како a симболи низ сите нив.
     280
     281Бидејќи почетниот кандидат клуч е составен токму од независните примитивни клучеви,
     282чии релации се спојуваат преку соодветните странски клучеви, на крајот се генерира
     283целосен ред составен само од a симболи. Ова докажува дека декомпозицијата е
     284Lossless Join.
     285
     286=== Чекор 2: 2NF -> 3NF (транзитивни зависности) ===
     287
     288За секоја R1-R17 се проверува дали не-клучен атрибут зависи од друг не-клучен атрибут.
     289
     290{{{
     291R1..R5, R7, R9..R17: секоја има единствен детерминант (сурогат/композитен PK);
     292                     сите не-клучни атрибути зависат директно од целиот клуч.
     293R6  (EnvSettings):   трите знамиња зависат само од (tenant_id, env_name), не едно
     294                     од друго -> нема транзитивност.
     295R8  (ComputerGroups):parent_group_id е FK (не детерминира ништо во R8) -> нема
     296                     транзитивност.
     297R9  (Computers):     network_id/group_id/env_name се FK-ови и не детерминираат
     298                     други атрибути во R9 -> нема транзитивност.
     299R14 (NetworkServices): постои алтернативен клуч (computer_id,port,protocol), но тоа е
     300                     клуч (superkey), не не-клучен детерминант -> нема транзитивност.
     301
     302Заклучок: сите R1-R17 се веќе во 3NF по завршување на Чекор 1.
    287303}}}
    288304
    289305=== Чекор 3: 3NF -> BCNF ===
    290306
    291 За BCNF, за секоја нетривијална FD X -> Y во релацијата, X мора да е superkey.
    292 
    293 {{{
    294 R1:  единствена FD е user_id -> ...           user_id е PK -> superkey       BCNF OK
    295 R2:  единствена FD е tenant_id -> ...         tenant_id е PK -> superkey     BCNF OK
    296 R3:  единствена FD е (user_id,tenant_id)->... тоа е PK -> superkey           BCNF OK
    297 R4:  единствена FD е env_id -> ...            env_id е PK -> superkey        BCNF OK
    298 R5:  единствена FD е env_token_id -> ...      env_token_id е PK -> superkey  BCNF OK
    299 R6:  единствена FD е (tenant_id,env_name)->.. тоа е PK -> superkey           BCNF OK
    300 R7:  единствена FD е computer_id -> ...       computer_id е PK -> superkey   BCNF OK
    301 R8:  единствена FD е process_id -> ...        process_id е PK -> superkey    BCNF OK
    302 R9:  единствена FD е history_id -> ...        history_id е PK -> superkey    BCNF OK
    303 R10: единствена FD е net_conn_id -> ...       net_conn_id е PK -> superkey   BCNF OK
    304 R11: единствена FD е alert_id -> ...          alert_id е PK -> superkey      BCNF OK
    305 R12: единствена FD е sysmon_event_id -> ...   sysmon_event_id е PK -> superkey BCNF OK
    306 
    307 Заклучок: сите R1-R12 се веќе во BCNF.
    308 Декомпозицијата завршува по еден единствен чекор (1NF -> 2NF), бидејќи
    309 истовремено ги отстранивме сите парцијални И транзитивни зависности.
     307За BCNF, за секоја нетривијална FD X -> Y, X мора да е superkey.
     308
     309{{{
     310R1..R13, R15..R17: единствената детерминанта во секоја е нејзиниот PK -> superkey. BCNF OK
     311R14 (NetworkServices): две нетривијални зависности -
     312       service_id -> ...                (PK, superkey)          BCNF OK
     313       (computer_id,port,protocol) -> service_id  (алтерн. клуч, superkey)  BCNF OK
     314
     315Заклучок: сите R1-R17 се во BCNF.
     316Декомпозицијата завршува по еден чекор (1NF -> 2NF), бидејќи истовремено ги
     317отстранивме сите парцијални И транзитивни зависности.
    310318}}}
    311319
     
    314322== d) Final Result and Discussion ==
    315323
    316 === Финален нормализиран дизајн ===
     324=== Финален нормализиран дизајн (BCNF) ===
    317325
    318326{{{
     
    322330Environments(env_id, tenant_id, env_name, env_created_at)
    323331EnvTokens(env_token_id, tenant_id, env_name, token, et_created_at, expires_at)
    324 EnvSettings(tenant_id, env_name, save_process_history, es_created_at, es_updated_at)
     332EnvSettings(tenant_id, env_name, save_process_history, save_metrics_history,
     333            save_network_history, es_created_at, es_updated_at)
     334Networks(network_id, tenant_id, env_name, network_name, cidr, gateway_ip, vlan)
     335ComputerGroups(group_id, tenant_id, group_name, group_description, parent_group_id)
    325336Computers(computer_id, tenant_id, env_name, computer_name, computer_user,
    326           computer_ip, computer_os, first_seen, last_seen, sysmon_available)
     337          computer_ip, computer_os, first_seen, last_seen, sysmon_available,
     338          network_id, group_id)
     339NetworkDevices(device_id, network_id, device_name, device_type, device_ip,
     340               mac, device_status)
    327341Processes(process_id, computer_id, pid, process_name, cpu_percent, memory_mb,
    328342          proc_username, cmdline, proc_timestamp)
    329343ComputerHistory(history_id, computer_id, cpu_usage, ram_usage, disk_usage,
    330                  network_sent_mb, network_recv_mb, hist_timestamp)
    331 NetworkConnections(net_conn_id, computer_id, pid, local_address, remote_address,
    332                     status, nc_process_name, nc_timestamp)
     344                network_sent_mb, network_recv_mb, hist_timestamp)
     345NetworkConnections(net_conn_id, computer_id, nc_pid, local_address, remote_address,
     346                   conn_status, nc_process_name, nc_timestamp)
     347NetworkServices(service_id, computer_id, service_name, port, protocol,
     348                service_status, last_checked)
     349ServiceAvailability(availability_id, service_id, is_available, response_time_ms,
     350                    sa_timestamp)
    333351SecurityAlerts(alert_id, computer_id, alert_type, severity, description,
    334                 alert_timestamp, resolved)
     352               alert_timestamp, resolved)
    335353SysmonEvents(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 Постои една значајна структурна разлика помеѓу теоретскиот модел добиен со чиста нормализација и реалната имплементација од Фаза 2 на проектот. Во теоретски нормализираниот модел, постои само една релација за процеси: `Processes (R8)`. Меѓутоа, во физичкиот SQL DDL од Фаза 2, овој ентитет е поделен на три посебни табели: `computer_processes`, `computer_processes_current` и `computer_processes_history`.
    346 
    347 Оваа одлука не е денормализација во негативна смисла, туку е свесна архитектурна оптимизација за подобри перформанси. Во сигурносен мониторинг систем, табелата со тековни процеси (`_current`) се ажурира на секои неколку секунди и бара брз упис и читање, додека историската табела (`_history`) содржи милиони записи и служи за ретроспективна анализа. Нивното физичко раздвојување спречува заклучување на табелите (table locking) и го оптимизира просторот. Логичката структура на атрибутите во сите три табели е идентична и комплетно еквивалентна на нормализираната форма R8.
    348 
    349 Заклучок: дизајнот од Фаза 2 останува дизајнот кој ќе се користи во следните фази на проектот, бидејќи е веќе во BCNF и оваа анализа само формално го потврдува тоа преку Chasing алгоритмот.
     354             sysmon_timestamp, details)
     355}}}
     356
     357Сите 17 логички релации се во BCNF, со зачувани функционални зависности (FD
     358preservation) и без губење на податоци при join (lossless join).
     359
     360=== Дискусија: споредба со имплементираниот v04 дизајн ===
     361
     362Формалната normalization постапка (од единствена денормализирана универзална
     363релација, преку attribute closure и chasing анализа) резултира со дизајн кој е
     364речиси идентичен со реалната имплементирана v04 шема. Ова потврдува дека дизајнот
     365од почеток бил близу оптимален (BCNF), без непотребна редундантност.
     366
     367Разлики помеѓу теоретскиот модел и физичката имплементација:
     368
     3691. '''Раздвојување тековна/историска состојба.''' Во нормализираниот модел постои
     370   по една логичка релација: Processes (R11), ComputerHistory (R12) и
     371   NetworkConnections (R13). Во физичкиот дизајн, секоја од нив е поделена на две
     372   табели со ИДЕНТИЧНА структура на атрибути:
     373     * computer_processes_current  / computer_processes_history
     374     * computer_history_current    / computer_history
     375     * network_connections_current / network_connections_history
     376   Ова НЕ е денормализација, туку свесна архитектурна оптимизација: табелата
     377   `_current` држи само последен snapshot (брз упис/читање, се препишува), додека
     378   `_history` расте со милиони записи за ретроспективна анализа. Дали се полни
     379   историјата се контролира по околина преку знамињата save_process_history,
     380   save_metrics_history и save_network_history во EnvSettings (R6). Логичката
     381   структура на атрибутите е идентична и еквивалентна на нормализираните R11/R12/R13.
     382
     3832. '''Странски клучеви наместо изведени вредности.''' Атрибутите tenant_id, env_name,
     384   network_id, group_id, computer_id, service_id - кои во универзалната релација беа
     385   изведливи (redundant) - во физичкиот дизајн се реализираат како странски клучеви
     386   што ги реализираат BCNF релациите преку референци (нормален, посакуван резултат).
     387
     3883. '''Само-референца.''' ComputerGroups.parent_group_id е странски клуч кон истата
     389   табела (хиерархија на групи). Тоа не нарушува BCNF - parent_group_id е обичен
     390   не-клучен атрибут што зависи само од group_id.
     391
     3924. '''Алтернативен клуч.''' NetworkServices има уникатност (computer_id, port,
     393   protocol) покрај сурогат клучот service_id - алтернативен candidate key, во склад
     394   со BCNF.
     395
     396Заклучок: имплементираниот v04 дизајн останува дизајнот што ќе се користи во
     397следните фази, бидејќи е веќе во BCNF; оваа анализа само формално го потврдува тоа
     398преку attribute closure и chasing алгоритмот.