= Нормализација - TradingMK = == Де-нормализирана форма на базата == === Универзална релација === Тргнуваме од една единствена универзална релација која ги содржи сите атрибути од целиот домен на апликацијата — сите табели споени во еден единствен запис. Атрибутите се преименувани каде е потребно за да нема дупликати на имиња. {{{ Universal_Relation_TradingMK( user_id, username, password, email, role, auth_provider, portfolio_id, balance, holding_id, holding_quantity, avg_price, stock_id, stock_symbol, stock_name, current_price, last_price, percentage, turnover, last_updated, history_id, history_price, history_timestamp, trade_id, trade_type, trade_status, trade_quantity, price_per_unit, trade_timestamp, trade_stock_symbol, transaction_id, txn_type, txn_quantity, txn_price, txn_timestamp, txn_origin, watchlist_id, price_above, price_below, oauth_token, oauth_email, oauth_provider, oauth_expires_at, oauth_created_at ) }}} '''Напомени за именување:''' * trade_stock_symbol е преименуван (наспроти stock_symbol) за да избегне конфликт на имиња во универзалната релација * auth_provider е атрибут кој претставува вредност од user_auth_providers - повеќевредносен по природа * Нема дупликати на имиња на атрибути во универзалната релација === Функционални зависности === Ги идентификуваме сите функционални зависности кои важат глобално во универзалната релација. '''F = {''' * '''FD1:''' user_id → username, password, email, role * '''FD2:''' username → user_id * '''FD3:''' email → user_id * '''FD4:''' portfolio_id → balance, user_id * '''FD5:''' user_id → portfolio_id * '''FD6:''' stock_id → stock_symbol, stock_name, current_price, last_price, percentage, turnover, last_updated * '''FD7:''' stock_symbol → stock_id * '''FD8:''' holding_id → holding_quantity, avg_price, portfolio_id, stock_id * '''FD9:''' history_id → history_price, history_timestamp, stock_id * '''FD10:''' trade_id → trade_type, trade_status, trade_quantity, price_per_unit, trade_timestamp, trade_stock_symbol, portfolio_id * '''FD11:''' transaction_id → txn_type, txn_quantity, txn_price, txn_timestamp, txn_origin, user_id, stock_id * '''FD12:''' watchlist_id → price_above, price_below, user_id, stock_id * '''FD13:''' oauth_token → oauth_email, oauth_provider, oauth_expires_at, oauth_created_at, user_id '''}''' === Кандидат клучеви и примарен клуч === ==== Класификација на атрибути (Лева / Десна страна) ==== ||= Атрибут =||= Лева страна =||= Десна страна =||= Класификација =|| || user_id || ✓ (FD1, FD5) || ✓ (FD2, FD3, FD4, FD11, FD12, FD13) || и лево и десно || || username || ✓ (FD2) || ✓ (FD1) || и лево и десно || || password || ✗ || ✓ (FD1) || само десно || || email || ✓ (FD3) || ✓ (FD1) || и лево и десно || || role || ✗ || ✓ (FD1) || само десно || || auth_provider || ✗ || ✗ || '''не се јавува во ниедна FD''' || || portfolio_id || ✓ (FD4) || ✓ (FD5, FD8, FD10) || и лево и десно || || balance || ✗ || ✓ (FD4) || само десно || || holding_id || ✓ (FD8) || ✗ || '''само лево''' || || holding_quantity || ✗ || ✓ (FD8) || само десно || || avg_price || ✗ || ✓ (FD8) || само десно || || stock_id || ✓ (FD6) || ✓ (FD7, FD8, FD9, FD11, FD12) || и лево и десно || || stock_symbol || ✓ (FD7) || ✓ (FD6) || и лево и десно || || stock_name || ✗ || ✓ (FD6) || само десно || || current_price || ✗ || ✓ (FD6) || само десно || || last_price || ✗ || ✓ (FD6) || само десно || || percentage || ✗ || ✓ (FD6) || само десно || || turnover || ✗ || ✓ (FD6) || само десно || || last_updated || ✗ || ✓ (FD6) || само десно || || history_id || ✓ (FD9) || ✗ || '''само лево''' || || history_price || ✗ || ✓ (FD9) || само десно || || history_timestamp || ✗ || ✓ (FD9) || само десно || || trade_id || ✓ (FD10) || ✗ || '''само лево''' || || trade_type || ✗ || ✓ (FD10) || само десно || || trade_status || ✗ || ✓ (FD10) || само десно || || trade_quantity || ✗ || ✓ (FD10) || само десно || || price_per_unit || ✗ || ✓ (FD10) || само десно || || trade_timestamp || ✗ || ✓ (FD10) || само десно || || trade_stock_symbol || ✗ || ✓ (FD10) || само десно || || transaction_id || ✓ (FD11) || ✗ || '''само лево''' || || txn_type || ✗ || ✓ (FD11) || само десно || || txn_quantity || ✗ || ✓ (FD11) || само десно || || txn_price || ✗ || ✓ (FD11) || само десно || || txn_timestamp || ✗ || ✓ (FD11) || само десно || || txn_origin || ✗ || ✓ (FD11) || само десно || || watchlist_id || ✓ (FD12) || ✗ || '''само лево''' || || price_above || ✗ || ✓ (FD12) || само десно || || price_below || ✗ || ✓ (FD12) || само десно || || oauth_token || ✓ (FD13) || ✗ || '''само лево''' || || oauth_email || ✗ || ✓ (FD13) || само десно || || oauth_provider || ✗ || ✓ (FD13) || само десно || || oauth_expires_at || ✗ || ✓ (FD13) || само десно || || oauth_created_at || ✗ || ✓ (FD13) || само десно || ==== Атрибути кои се појавуваат САМО на лева страна ==== Овие атрибути мора да бидат дел од секој кандидат клуч, бидејќи не можат да се изведат од ниедна FD: * '''holding_id''' - мора да биде дел од секој кандидат клуч * '''history_id''' - мора да биде дел од секој кандидат клуч * '''trade_id''' - мора да биде дел од секој кандидат клуч * '''transaction_id''' - мора да биде дел од секој кандидат клуч * '''watchlist_id''' - мора да биде дел од секој кандидат клуч * '''oauth_token''' - мора да биде дел од секој кандидат клуч * '''auth_provider''' - мора да биде дел од секој кандидат клуч (не учествува во ниедна FD; изводливо само преку себе) ==== Пресметка на затворач ==== ''Чекор 1:''' Започнуваме со задолжителни: {{{ {holding_id, history_id, trade_id, transaction_id, watchlist_id, oauth_token, auth_provider}: - Од FD8 (holding_id →): holding_quantity, avg_price, portfolio_id, stock_id - Од FD9 (history_id →): history_price, history_timestamp, stock_id - Од FD10 (trade_id →): trade_type, trade_status, trade_quantity, price_per_unit, trade_timestamp, trade_stock_symbol, portfolio_id - Од FD11 (transaction_id →): txn_type, txn_quantity, txn_price, txn_timestamp, txn_origin, user_id, stock_id - Од FD12 (watchlist_id →): price_above, price_below, user_id, stock_id - Од FD13 (oauth_token →): oauth_email, oauth_provider, oauth_expires_at, oauth_created_at, user_id - Од FD4 (portfolio_id →): balance, user_id - Од FD5 (user_id →): portfolio_id - Од FD1 (user_id →): username, password, email, role - Од FD6 (stock_id →): stock_symbol, stock_name, current_price, last_price, percentage, turnover, last_updated - Од FD7 (stock_symbol →): stock_id - Од FD2 (username →): user_id - Од FD3 (email →): user_id }}} {{{ {holding_id, history_id, trade_id, transaction_id, watchlist_id, oauth_token, auth_provider} = Universal_Relation_TradingMK ✓ (сите атрибути се изводливи) }}} ==== Проверка за минималност ==== ||= Подмножество (отстранет атрибут) =||= Недостасуваат атрибути =||= Суперклуч? =|| || без holding_id || holding_quantity, avg_price || ✗ НЕ || || без history_id || history_price, history_timestamp || ✗ НЕ || || без trade_id || trade_type, trade_status, trade_quantity, price_per_unit, trade_timestamp, trade_stock_symbol || ✗ НЕ || || без transaction_id || txn_type, txn_quantity, txn_price, txn_timestamp, txn_origin || ✗ НЕ || || без watchlist_id || price_above, price_below || ✗ НЕ || || без oauth_token || oauth_email, oauth_expires_at, oauth_created_at || ✗ НЕ || || без auth_provider || auth_provider не е изводливо || ✗ НЕ || Секој атрибут е неопходен - множеството е минимално. ==== Избор на примарен клуч ==== ''Кандидат клуч (и избран примарен клуч):''' {{{ PK = {holding_id, history_id, trade_id, transaction_id, watchlist_id, oauth_token, auth_provider} }}} '''Образложение:''' Примарниот клуч е голем бидејќи универзалната релација содржи повеќе независни ентитети (корисници, портфолија, акции, трговски барања, трансакции, watchlist записи, OAuth токени, auth провајдери) кои не се директно поврзани преку функционални зависности. Секој независен ентитет бара барем еден идентификатор во клучот. Клучот е минимален - отстранувањето на кој било атрибут го прави невозможно изведувањето на дел од атрибутите. ==== Нормална форма пред декомпозиција ==== Universal_Relation_TradingMK е во '''1NF''' (сите атрибути се атомарни, со исклучок на auth_provider кој е мулти-вредносен). Релацијата '''НЕ е во 2NF''' поради следните парцијални зависности - секој идентификатор ги определува само своите атрибути, независно од останатиот примарен клуч: * FD8: holding_id → holding_quantity, avg_price, portfolio_id, stock_id * FD9: history_id → history_price, history_timestamp, stock_id * FD10: trade_id → trade_type, trade_status, ..., portfolio_id * FD11: transaction_id → txn_type, ..., user_id, stock_id * FD12: watchlist_id → price_above, price_below, user_id, stock_id * FD13: oauth_token → oauth_email, oauth_provider, oauth_expires_at, oauth_created_at, user_id ---- == 1NF Декомпозиција == === Анализа === '''Анализирана релација:''' Universal_Relation_TradingMK '''Функционални зависности:''' FD1 - FD13 (сите) '''Кандидат клуч / Примарен клуч:''' {holding_id, history_id, trade_id, transaction_id, watchlist_id, oauth_token, auth_provider} '''Нормална форма:''' Релацијата е делумно во 1NF - атрибутот '''auth_provider''' претставува повеќевредносен (multi-valued) атрибут. Еден корисник може да има повеќе auth провајдери (интерен, Google), па оваа вредност не може да биде атомарна во унификованата релација. '''Зависност која го предизвикува проблемот:''' auth_provider е неделива повторувачка вредност поврзана со user_id - нема функционална зависност од единечен детерминант, туку претставува множество вредности. === Декомпозиција - Извлекување на auth_provider === {{{ User_Auth_Providers(user_id, auth_provider) PK: {user_id, auth_provider} FK: user_id → Users Universal_Relation_Base = Universal_Relation_TradingMK - {auth_provider} PK: {holding_id, history_id, trade_id, transaction_id, watchlist_id, oauth_token} }}} '''Функционални зависности во User_Auth_Providers:''' нема FD освен дефиницијата на PK. '''Функционални зависности во Universal_Relation_Base:''' FD1 – FD13 (auth_provider отстранет). '''Кандидат клучеви во Universal_Relation_Base:''' {holding_id, history_id, trade_id, transaction_id, watchlist_id, oauth_token} '''Проверка за lossless join:''' Декомпозицијата е lossless бидејќи заедничкиот атрибут user_id е клуч во Users и FK во User_Auth_Providers — секој auth_provider запис може недвосмислено да се поврзе со корисник. ✓ '''Проверка за dependency preservation:''' Нема FD која ги поврзува само auth_provider атрибутите — релацијата е дефинирана преку бизнис логика (1:N врска). Сите 13 FD остануваат во Universal_Relation_Base. ✓ '''Заклучок:''' Universal_Relation_Base е во 1NF. Продолжуваме со 2NF декомпозиција. ---- == 2NF Декомпозиција == === Дефиниција === Релацијата е во 2NF ако е во 1NF и секој не-клучен атрибут целосно функционално зависи од целиот примарен клуч - нема парцијални зависности. === Анализа === '''Анализирана релација:''' Universal_Relation_Base '''Примарен клуч:''' {holding_id, history_id, trade_id, transaction_id, watchlist_id, oauth_token} '''Нормална форма:''' НЕ е во 2NF. Следните FDs претставуваат парцијални зависности: ||= FD =||= Детерминант =||= Дел од PK? =||= Парцијална зависност? =|| || FD8: holding_id → holding_quantity, avg_price, portfolio_id, stock_id || holding_id || ДА || '''ДА''' || || FD9: history_id → history_price, history_timestamp, stock_id || history_id || ДА || '''ДА''' || || FD10: trade_id → trade_type, trade_status, trade_quantity, price_per_unit, trade_timestamp, trade_stock_symbol, portfolio_id || trade_id || ДА || '''ДА''' || || FD11: transaction_id → txn_type, txn_quantity, txn_price, txn_timestamp, txn_origin, user_id, stock_id || transaction_id || ДА || '''ДА''' || || FD12: watchlist_id → price_above, price_below, user_id, stock_id || watchlist_id || ДА || '''ДА''' || || FD13: oauth_token → oauth_email, oauth_provider, oauth_expires_at, oauth_created_at, user_id || oauth_token || ДА || '''ДА''' || || FD1-FD7: транзитивни преку горните детерминанти || не директно || НЕ || Не директно || Ги елиминираме по ред, почнувајќи со FD8. === Чекор 2.1: Декомпозиција по FD8 (holding_id → ...) === '''Нарушува 2NF:''' holding_id е дел од PK, а holding_quantity, avg_price, portfolio_id, stock_id зависат само од него. {{{ Portfolio_Holdings(holding_id, holding_quantity, avg_price, portfolio_id, stock_id) PK: holding_id FK: portfolio_id → Portfolios, stock_id → Stock Universal_Relation_Base_1 = Universal_Relation_Base - {holding_id, holding_quantity, avg_price} PK: {history_id, trade_id, transaction_id, watchlist_id, oauth_token} }}} '''FD во Portfolio_Holdings:''' FD8: holding_id → holding_quantity, avg_price, portfolio_id, stock_id '''Кандидат клучеви:''' {holding_id} '''Напомена:''' (portfolio_id, stock_id) НЕ е уникатен - еден портфолио може да чува исти акции во повеќе лотови. '''Lossless join:''' Заедничкиот атрибут holding_id е PK во новата релација - lossless. ✓ '''Dependency preservation:''' FD8 е зачувана во Portfolio_Holdings. ✓ === Чекор 2.2: Декомпозиција по FD9 (history_id → ...) === '''Нарушува 2NF:''' history_id е дел од PK, а history_price, history_timestamp, stock_id зависат само од него. {{{ Stock_History(history_id, history_price, history_timestamp, stock_id) PK: history_id FK: stock_id → Stock Universal_Relation_Base_2 = Universal_Relation_Base_1 - {history_id, history_price, history_timestamp} PK: {trade_id, transaction_id, watchlist_id, oauth_token} }}} '''FD во Stock_History:''' FD9: history_id → history_price, history_timestamp, stock_id '''Кандидат клучеви:''' {history_id} '''Lossless join:''' Заедничкиот атрибут history_id е PK - lossless. ✓ '''Dependency preservation:''' FD9 е зачувана во Stock_History. ✓ === Чекор 2.3: Декомпозиција по FD10 (trade_id → ...) === '''Нарушува 2NF:''' trade_id е дел од PK, а trade_type, trade_status, trade_quantity, price_per_unit, trade_timestamp, trade_stock_symbol, portfolio_id зависат само од него. {{{ Trade_Request(trade_id, trade_type, trade_status, trade_quantity, price_per_unit, trade_timestamp, trade_stock_symbol, portfolio_id) PK: trade_id FK: portfolio_id → Portfolios Universal_Relation_Base_3 = Universal_Relation_Base_2 - {trade_id, trade_type, trade_status, trade_quantity, price_per_unit, trade_timestamp, trade_stock_symbol} PK: {transaction_id, watchlist_id, oauth_token} }}} '''FD во Trade_Request:''' FD10: trade_id → trade_type, trade_status, trade_quantity, price_per_unit, trade_timestamp, trade_stock_symbol, portfolio_id '''Кандидат клучеви:''' {trade_id} '''Напомена:''' user_id НЕ е складиран во Trade_Request бидејќи е изводливо преку portfolio_id → Portfolios → Users, елиминирајќи ја редундантноста. '''Lossless join:''' Заедничкиот атрибут trade_id е PK - lossless. ✓ '''Dependency preservation:''' FD10 е зачувана во Trade_Request. ✓ === Чекор 2.4: Декомпозиција по FD11 (transaction_id → ...) === '''Нарушува 2NF:''' transaction_id е дел од PK, а txn_type, txn_quantity, txn_price, txn_timestamp, txn_origin, user_id, stock_id зависат само од него. {{{ Transactions(transaction_id, txn_type, txn_quantity, txn_price, txn_timestamp, txn_origin, user_id, stock_id) PK: transaction_id FK: user_id → Users, stock_id → Stock Universal_Relation_Base_4 = Universal_Relation_Base_3 - {transaction_id, txn_type, txn_quantity, txn_price, txn_timestamp, txn_origin} PK: {watchlist_id, oauth_token} }}} '''FD во Transactions:''' FD11: transaction_id → txn_type, txn_quantity, txn_price, txn_timestamp, txn_origin, user_id, stock_id '''Кандидат клучеви:''' {transaction_id} '''Lossless join:''' Заедничкиот атрибут transaction_id е PK - lossless. ✓ '''Dependency preservation:''' FD11 е зачувана во Transactions. ✓ === Чекор 2.5: Декомпозиција по FD12 (watchlist_id → ...) === '''Нарушува 2NF:''' watchlist_id е дел од PK, а price_above, price_below, user_id, stock_id зависат само од него. {{{ Watchlist(watchlist_id, price_above, price_below, user_id, stock_id) PK: watchlist_id FK: user_id → Users, stock_id → Stock Universal_Relation_Base_5 = Universal_Relation_Base_4 - {watchlist_id, price_above, price_below} PK: {oauth_token} }}} '''FD во Watchlist:''' FD12: watchlist_id → price_above, price_below, user_id, stock_id '''Кандидат клучеви:''' {watchlist_id} '''Напомена:''' (user_id, stock_id) НЕ е уникатен - еден корисник може да има повеќе alert-ови за иста акција. '''Lossless join:''' Заедничкиот атрибут watchlist_id е PK - lossless. ✓ '''Dependency preservation:''' FD12 е зачувана во Watchlist. ✓ === Чекор 2.6: Декомпозиција по FD13 (oauth_token → ...) === '''Нарушува 2NF:''' oauth_token е дел од PK, а oauth_email, oauth_provider, oauth_expires_at, oauth_created_at, user_id зависат само од него. {{{ OAuth_Pending_Links(oauth_token, oauth_email, oauth_provider, oauth_expires_at, oauth_created_at, user_id) PK: oauth_token FK: user_id → Users Universal_Relation_Base_6 = Universal_Relation_Base_5 - {oauth_token, oauth_email, oauth_provider, oauth_expires_at, oauth_created_at} Останати атрибути: (user_id, username, password, email, role, portfolio_id, balance, stock_id, stock_symbol, stock_name, current_price, last_price, percentage, turnover, last_updated) PK: ∅ }}} '''FD во OAuth_Pending_Links:''' FD13: oauth_token → oauth_email, oauth_provider, oauth_expires_at, oauth_created_at, user_id '''Кандидат клучеви:''' {oauth_token} '''Lossless join:''' Заедничкиот атрибут oauth_token е PK - lossless. ✓ '''Dependency preservation:''' FD13 е зачувана во OAuth_Pending_Links. ✓ === Состојба после 2NF декомпозиција === По извлекувањето на сите парцијални зависности, останатата релација Universal_Relation_Base_6 содржи три независни ентитети: {{{ Universal_Relation_Base_6( user_id, username, password, email, role, portfolio_id, balance, stock_id, stock_symbol, stock_name, current_price, last_price, percentage, turnover, last_updated ) PK: ∅ }}} Оваа релација веќе нема парцијални зависности, па продолжуваме директно со проверка за 3NF. Меѓутоа, содржи транзитивни зависности - нарушување на 3NF. {{{ Состојба по 2NF: User_Auth_Providers(user_id, auth_provider) — од 1NF Portfolio_Holdings(holding_id, holding_quantity, avg_price, portfolio_id, stock_id) Stock_History(history_id, history_price, history_timestamp, stock_id) Trade_Request(trade_id, trade_type, trade_status, trade_quantity, price_per_unit, trade_timestamp, trade_stock_symbol, portfolio_id) Transactions(transaction_id, txn_type, txn_quantity, txn_price, txn_timestamp, txn_origin, user_id, stock_id) Watchlist(watchlist_id, price_above, price_below, user_id, stock_id) OAuth_Pending_Links(oauth_token, oauth_email, oauth_provider, oauth_expires_at, oauth_created_at, user_id) Universal_Relation_Base_6(user_id, username, password, email, role, portfolio_id, balance, stock_id, stock_symbol, stock_name, current_price, last_price, percentage, turnover, last_updated) PK: ∅ }}} ---- == 3NF Декомпозиција == === Дефиниција === Релацијата е во 3NF ако е во 2NF и за секоја нетривијална FD X → A, или X е суперклуч, или A е примарен атрибут. Ова ги елиминира транзитивните зависности. prov === Анализа на Universal_Relation_Base_6 === '''Анализирана релација:''' Universal_Relation_Base_6 '''Применети FD:''' FD1, FD2, FD3, FD4, FD5, FD6, FD7 '''Нормална форма:''' НЕ е во 3NF. Постои транзитивна зависност: user_id → portfolio_id → balance (balance е транзитивно зависен од user_id преку portfolio_id). Stock атрибутите се целосно независни од User атрибутите. '''Зависности кои предизвикуваат проблем:''' * Транзитивна зависност: user_id → portfolio_id → balance (balance зависи транзитивно од user_id преку portfolio_id кој не е PK во оваа релација) * FD6 и FD7 формираат независен ентитет (Stock) кој не е функционално поврзан со User или Portfolio во оваа релација - мора да се извлече посебно === Чекор 3.1: Декомпозиција по FD1/FD2/FD3 (user_id → ...) === {{{ Users(user_id, username, password, email, role) PK: user_id Кандидат клучеви: {user_id}, {username}, {email} Universal_Relation_Base_7 = Universal_Relation_Base_6 - {username, password, email, role} (user_id останува за поврзување со Portfolios) }}} '''FD во Users:''' FD1: user_id → username, password, email, role; FD2: username → user_id; FD3: email → user_id '''Кандидат клучеви:''' {user_id}, {username}, {email} '''Нормална форма на Users:''' 3NF - сите детерминанти (user_id, username, email) се кандидат клучеви (суперклучеви). ✓ '''Lossless join:''' Заедничкиот атрибут user_id е PK во Users - lossless. ✓ '''Dependency preservation:''' FD1, FD2, FD3 зачувани. ✓ === Чекор 3.2: Декомпозиција по FD4/FD5 (portfolio_id ↔ user_id) === {{{ Portfolios(portfolio_id, balance, user_id) PK: portfolio_id Кандидат клучеви: {portfolio_id}, {user_id} (1:1 врска — секој корисник има точно едно портфолио) FK: user_id → Users Universal_Relation_Base_8 = Universal_Relation_Base_7 - {portfolio_id, balance, user_id} }}} '''FD во Portfolios:''' FD4: portfolio_id → balance, user_id; FD5: user_id → portfolio_id '''Кандидат клучеви:''' {portfolio_id}, {user_id} '''Нормална форма на Portfolios:''' 3NF — двата детерминанти (portfolio_id и user_id) се кандидат клучеви. ✓ '''Lossless join:''' Заедничкиот атрибут user_id/portfolio_id — lossless. ✓ '''Dependency preservation:''' FD4, FD5 зачувани. ✓ === Чекор 3.3: Декомпозиција по FD6/FD7 (stock_id ↔ stock_symbol) === {{{ Stock(stock_id, stock_symbol, stock_name, current_price, last_price, percentage, turnover, last_updated) PK: stock_id Кандидат клучеви: {stock_id}, {stock_symbol} Universal_Relation_Base_9 = Universal_Relation_Base_8 - {stock_id, stock_symbol, stock_name, current_price, last_price, percentage, turnover, last_updated} = ∅ (сите атрибути се распоредени) }}} '''FD во Stock:''' FD6: stock_id → stock_symbol, stock_name, current_price, last_price, percentage, turnover, last_updated; FD7: stock_symbol → stock_id '''Кандидат клучеви:''' {stock_id}, {stock_symbol} '''Нормална форма на Stock:''' 3NF - двата детерминанти (stock_id и stock_symbol) се кандидат клучеви. ✓ '''Lossless join:''' Заедничкиот атрибут stock_id - lossless. '''Dependency preservation:''' FD6, FD7 зачувани. ✓ Universal_Relation_Base_9 е празна (∅) - сите атрибути се успешно распоредени. === Проверка за транзитивни зависности во сите релации === ||= Релација =||= Проверка =||= 3NF? =|| || User_Auth_Providers || Нема не-клучни атрибути - само PK || ✓ ДА || || Portfolio_Holdings || holding_id → сите; portfolio_id и stock_id не одредуваат меѓусебно никој не-клучен атрибут || ✓ ДА || || Stock_History || history_id → сите; stock_id не одредува history_price/timestamp во оваа релација || ✓ ДА || || Trade_Request || trade_id → сите; нема транзитивни зависности || ✓ ДА || || Transactions || transaction_id → сите; нема транзитивни зависности || ✓ ДА || || Watchlist || watchlist_id → сите; нема транзитивни зависности || ✓ ДА || || OAuth_Pending_Links || oauth_token → сите; нема транзитивни зависности || ✓ ДА || || Users || user_id, username, email - сите се кандидат клучеви → без транзитивност || ✓ ДА || || Portfolios || portfolio_id, user_id - сите се кандидат клучеви → без транзитивност || ✓ ДА || || Stock || stock_id, stock_symbol - сите се кандидат клучеви → без транзитивност || ✓ ДА || '''Заклучок:''' Сите 10 релации се во 3NF. ✓ ---- == BCNF Декомпозиција == === Дефиниција === Релацијата е во BCNF ако за секоја нетривијална FD X → Y, X е суперклуч. BCNF е построга од 3NF; нарушувања можат да се појават само кај релации со повеќе преклопувачки кандидат клучеви. === Проверка за BCNF === Ги проверуваме само релациите со повеќе кандидат клучеви: ==== Users - Кандидат клучеви: {user_id}, {username}, {email} ==== ||= FD =||= Детерминант =||= Суперклуч? =|| || user_id → username, password, email, role || user_id || ✓ ДА || || username → user_id || username || ✓ ДА || || email → user_id || email || ✓ ДА || '''Users е во BCNF ✓''' ==== Portfolios - Кандидат клучеви: {portfolio_id}, {user_id} ==== ||= FD =||= Детерминант =||= Суперклуч? =|| || portfolio_id → balance, user_id || portfolio_id || ✓ ДА || || user_id → portfolio_id, balance || user_id || ✓ ДА || '''Portfolios е во BCNF ✓''' ==== Stock - Кандидат клучеви: {stock_id}, {stock_symbol} ==== ||= FD =||= Детерминант =||= Суперклуч? =|| || stock_id → сите атрибути || stock_id || ✓ ДА || || stock_symbol → stock_id и транзитивно сите || stock_symbol || ✓ ДА || '''Stock е во BCNF ✓''' ==== Останати 7 релации ==== User_Auth_Providers, Portfolio_Holdings, Stock_History, Trade_Request, Transactions, Watchlist, OAuth_Pending_Links - секоја има точно еден кандидат клуч. Единствената нетривијална FD го има PK-от како детерминант. '''Сите се во BCNF ✓''' '''Заклучок: Сите 10 релации се во BCNF - највисоката нормална форма постигната. ✓''' ---- == Финален резултат и дискусија == === Нормализиран релационен модел === {{{ Users(user_id, username, password, email, role) - Примарен клуч: user_id - Кандидат клучеви: {user_id}, {username}, {email} - Надворешни клучеви: - User_Auth_Providers(user_id, auth_provider) - Примарен клуч: {user_id, auth_provider} - Кандидат клучеви: {user_id, auth_provider} - Надворешни клучеви: user_id → Users Portfolios(portfolio_id, balance, user_id) - Примарен клуч: portfolio_id - Кандидат клучеви: {portfolio_id}, {user_id} - Надворешни клучеви: user_id → Users - Напомена: 1:1 врска со Users - секој корисник има точно едно портфолио Stock(stock_id, stock_symbol, stock_name, current_price, last_price, percentage, turnover, last_updated) - Примарен клуч: stock_id - Кандидат клучеви: {stock_id}, {stock_symbol} - Надворешни клучеви: — Portfolio_Holdings(holding_id, holding_quantity, avg_price, portfolio_id, stock_id) - Примарен клуч: holding_id - Кандидат клучеви: {holding_id} - Надворешни клучеви: portfolio_id → Portfolios, stock_id → Stock - Напомена: (portfolio_id, stock_id) НЕ е уникатен - еден портфолио може да чува исти акции во повеќе места Stock_History(history_id, history_price, history_timestamp, stock_id) - Примарен клуч: history_id - Кандидат клучеви: {history_id} - Надворешни клучеви: stock_id → Stock Trade_Request(trade_id, trade_type, trade_status, trade_quantity, price_per_unit, trade_timestamp, trade_stock_symbol, portfolio_id) - Примарен клуч: trade_id - Кандидат клучеви: {trade_id} - Надворешни клучеви: portfolio_id → Portfolios - Напомена: user_id е изводливо преку portfolio_id → Portfolios → Users Transactions(transaction_id, txn_type, txn_quantity, txn_price, txn_timestamp, txn_origin, user_id, stock_id) - Примарен клуч: transaction_id - Кандидат клучеви: {transaction_id} - Надворешни клучеви: user_id → Users, stock_id → Stock Watchlist(watchlist_id, price_above, price_below, user_id, stock_id) - Примарен клуч: watchlist_id - Кандидат клучеви: {watchlist_id} - Надворешни клучеви: user_id → Users, stock_id → Stock - Напомена: (user_id, stock_id) НЕ е уникатен - еден корисник може да има повеќе alerт-ови за иста акција OAuth_Pending_Links(oauth_token, oauth_email, oauth_provider, oauth_expires_at, oauth_created_at, user_id) - Примарен клуч: oauth_token - Кандидат клучеви: {oauth_token} - Надворешни клучеви: user_id → Users }}} === Дискусија === ==== Клучни наоди ==== 1. '''Процесот на нормализација го потврди дизајнот''' - декомпозицијата базирана на функционалните зависности резултираше со релации кои одговараат на ентитетите од оригиналниот ER модел од Фаза P2. 2. '''Декомпозицијата е lossless''' - на секој чекор оригиналната информација може да се реконструира преку JOIN операции на заедничките атрибути (надворешни клучеви). Ова е гарантирано со Heath теоремата, бидејќи секоја декомпозиција се врши по FD чиј детерминант е клуч во новата релација. 3. '''Сите функционални зависности се зачувани''' - сите 13 функционални зависности (FD1-FD13) се зачувани во соодветните релации, без потреба за скапи JOIN операции при проверка на ограничувањата. ==== Кој дизајн ќе се користи понатаму ==== Финалниот нормализиран дизајн (Фаза P5) ќе се користи во сите понатамошни фази на проектот. Тој е идентичен со дизајнот од Фаза P2 со следните конкретни измени во имплементацијата: * Додавање на UNIQUE ограничувачи на stock.symbol