= Нормализација - TradingMK = == Де-нормализирана форма на базата == === Универзална релација === Тргнуваме од една единствена универзална релација која ги содржи сите атрибути од целиот домен на апликацијата сите табели споени во еден единствен запис. Атрибутите се преименувани каде е потребно за да нема дупликати на имиња. {{{ Universal_Relation_TradingMK( user_id, username, password, email, role, auth_provider_id, 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_id, transaction_id, txn_type, txn_quantity, txn_price, txn_timestamp, txn_origin, trade_request_id, watchlist_id, price_above, price_below, oauth_token, oauth_email, oauth_provider, oauth_expires_at, oauth_created_at, oauth_auth_provider_id ) }}} '''Напомени за именување:''' * trade_stock_id е преименуван за јасно да се разликува од stock_id во останатите делови на универзалната релација * auth_provider_id е идентификатор на AuthProvider ентитетот * oauth_auth_provider_id е преименуван за да се избегне конфликт со auth_provider_id во универзалната релација * Нема дупликати на имиња на атрибути во универзалната релација === Функционални зависности === Ги идентификуваме сите функционални зависности кои важат глобално во универзалната релација. '''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_id, portfolio_id * '''FD11:''' transaction_id → txn_type, txn_quantity, txn_price, txn_timestamp, txn_origin, user_id, stock_id, trade_request_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, oauth_auth_provider_id * '''FD14:''' auth_provider_id → auth_provider '''}''' === Кандидат клучеви и примарен клуч === ==== Класификација на атрибути (Лева / Десна страна) ==== ||= Атрибут =||= Лева страна =||= Десна страна =||= Класификација =|| || user_id || ✓ (FD1, FD5) || ✓ (FD2, FD3, FD4, FD11, FD12, FD13) || и лево и десно || || username || ✓ (FD2) || ✓ (FD1) || и лево и десно || || password || ✗ || ✓ (FD1) || само десно || || email || ✓ (FD3) || ✓ (FD1) || и лево и десно || || role || ✗ || ✓ (FD1) || само десно || || auth_provider_id || ✓ (FD14) || ✓ (FD13) || и лево и десно || || auth_provider || ✗ || ✓ (FD14) || само десно || || 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_id || ✗ || ✓ (FD10) || само десно || || transaction_id || ✓ (FD11) || ✗ || '''само лево''' || || txn_type || ✗ || ✓ (FD11) || само десно || || txn_quantity || ✗ || ✓ (FD11) || само десно || || txn_price || ✗ || ✓ (FD11) || само десно || || txn_timestamp || ✗ || ✓ (FD11) || само десно || || txn_origin || ✗ || ✓ (FD11) || само десно || || trade_request_id || ✗ || ✓ (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) || само десно || || oauth_auth_provider_id || ✗ || ✓ (FD13) || само десно || ==== Атрибути кои се појавуваат САМО на лева страна ==== Овие атрибути мора да бидат дел од секој кандидат клуч, бидејќи не можат да се изведат од ниедна FD: * '''holding_id''' - мора да биде дел од секој кандидат клуч * '''history_id''' - мора да биде дел од секој кандидат клуч * '''trade_id''' - мора да биде дел од секој кандидат клуч * '''transaction_id''' - мора да биде дел од секој кандидат клуч * '''watchlist_id''' - мора да биде дел од секој кандидат клуч * '''oauth_token''' - мора да биде дел од секој кандидат клуч ==== Пресметка на затворач ==== ''Чекор 1:''' Започнуваме со задолжителни: {{{ {holding_id, history_id, trade_id, transaction_id, watchlist_id, oauth_token}: - Од 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_id, portfolio_id - Од FD11 (transaction_id →): txn_type, txn_quantity, txn_price, txn_timestamp, txn_origin, user_id, stock_id, trade_request_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, oauth_auth_provider_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 - Од FD14 (auth_provider_id →): auth_provider }}} {{{ {holding_id, history_id, trade_id, transaction_id, watchlist_id, oauth_token} = 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_id || ✗ НЕ || || без transaction_id || txn_type, txn_quantity, txn_price, txn_timestamp, txn_origin, trade_request_id || ✗ НЕ || || без watchlist_id || price_above, price_below || ✗ НЕ || || без oauth_token || oauth_email, oauth_expires_at, oauth_created_at, oauth_auth_provider_id || ✗ НЕ || Секој атрибут е неопходен - множеството е минимално. ==== Избор на примарен клуч ==== ''Кандидат клуч (и избран примарен клуч):''' {{{ PK = {holding_id, history_id, trade_id, transaction_id, watchlist_id, oauth_token} }}} '''Образложение:''' Примарниот клуч е голем бидејќи универзалната релација содржи повеќе независни ентитети (корисници, портфолија, акции, трговски барања, трансакции, watchlist записи, OAuth токени и auth провајдери) кои не се директно поврзани преку функционални зависности. Секој независен ентитет бара барем еден идентификатор во клучот. Клучот е минимален - отстранувањето на кој било атрибут го прави невозможно изведувањето на дел од атрибутите. ==== Нормална форма пред декомпозиција ==== Universal_Relation_TradingMK е во '''1NF''' бидејќи сите атрибути се атомарни. AuthProvider е моделиран како посебен ентитет преку auth_provider_id, па 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, trade_request_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, oauth_auth_provider_id * FD14: auth_provider_id → auth_provider ---- == 2NF Декомпозиција == === Дефиниција === Релацијата е во 2NF ако е во 1NF и секој не-клучен атрибут целосно функционално зависи од целиот примарен клуч - нема парцијални зависности. === Анализа === '''Анализирана релација:''' Universal_Relation_TradingMK '''Примарен клуч:''' {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_id, portfolio_id || trade_id || ДА || '''ДА''' || || FD11: transaction_id → txn_type, txn_quantity, txn_price, txn_timestamp, txn_origin, user_id, stock_id, trade_request_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_auth_provider_id || oauth_token || ДА || '''ДА''' || || FD14: auth_provider_id → auth_provider || auth_provider_id || НЕ || Не парцијална зависност || Ги елиминираме по ред, почнувајќи со 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_TradingMK - {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_id, portfolio_id зависат само од него. {{{ Trade_Request(trade_id, trade_type, trade_status, trade_quantity, price_per_unit, trade_timestamp, trade_stock_id, portfolio_id) PK: trade_id FK: portfolio_id → Portfolios, trade_stock_id → Stock Universal_Relation_Base_3 = Universal_Relation_Base_2 - {trade_id, trade_type, trade_status, trade_quantity, price_per_unit, trade_timestamp, trade_stock_id} 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_id, 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, trade_request_id зависат само од него. {{{ Transactions(transaction_id, txn_type, txn_quantity, txn_price, txn_timestamp, txn_origin, user_id, stock_id, trade_request_id) PK: transaction_id FK: user_id → Users, stock_id → Stock, trade_request_id → Trade_Request Universal_Relation_Base_4 = Universal_Relation_Base_3 - {transaction_id, txn_type, txn_quantity, txn_price, txn_timestamp, txn_origin, trade_request_id} PK: {watchlist_id, oauth_token} }}} '''FD во Transactions:''' FD11: transaction_id → txn_type, txn_quantity, txn_price, txn_timestamp, txn_origin, user_id, stock_id, trade_request_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_auth_provider_id зависат само од него. {{{ OAuth_Pending_Links(oauth_token, oauth_email, oauth_provider, oauth_expires_at, oauth_created_at, user_id, oauth_auth_provider_id) PK: oauth_token FK: user_id → Users, oauth_auth_provider_id → AuthProvider Universal_Relation_Base_6 = Universal_Relation_Base_5 - {oauth_token, oauth_email, oauth_provider, oauth_expires_at, oauth_created_at, oauth_auth_provider_id} Останати атрибути: (user_id, username, password, email, role, auth_provider_id, auth_provider, 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_auth_provider_id '''Кандидат клучеви:''' {oauth_token} '''Lossless join:''' Заедничкиот атрибут oauth_token е PK - lossless. ✓ '''Dependency preservation:''' FD13 е зачувана во OAuth_Pending_Links. ✓ === Чекор 2.7: Декомпозиција по FD14 (auth_provider_id → auth_provider) === '''FD14:''' auth_provider_id → auth_provider Бидејќи auth_provider_id не е дел од примарниот клуч на Universal_Relation_Base_6, FD14 не претставува парцијална зависност и се обработува во 3NF како зависност од независен ентитет. ---- === Состојба после 2NF декомпозиција === По извлекувањето на сите парцијални зависности, останатата релација Universal_Relation_Base_6 содржи независни ентитети: {{{ Universal_Relation_Base_6( user_id, username, password, email, role, auth_provider_id, auth_provider, portfolio_id, balance, stock_id, stock_symbol, stock_name, current_price, last_price, percentage, turnover, last_updated ) PK: ∅ }}} Оваа релација веќе нема парцијални зависности, па продолжуваме директно со проверка за 3NF. Меѓутоа, содржи транзитивни зависности и независни ентитети - нарушување на 3NF. {{{ Состојба по 2NF: 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_id, portfolio_id) Transactions(transaction_id, txn_type, txn_quantity, txn_price, txn_timestamp, txn_origin, user_id, stock_id, trade_request_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, oauth_auth_provider_id) Universal_Relation_Base_6(user_id, username, password, email, role, auth_provider_id, auth_provider, 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 е примарен атрибут. Ова ги елиминира транзитивните зависности. === Анализа на Universal_Relation_Base_6 === '''Анализирана релација:''' Universal_Relation_Base_6 '''Применети FD:''' FD1, FD2, FD3, FD4, FD5, FD6, FD7, FD14 '''Нормална форма:''' НЕ е во 3NF. Постои транзитивна зависност: user_id → portfolio_id → balance (balance е транзитивно зависен од user_id преку portfolio_id кој не е PK во оваа релација). Исто така, постои зависност auth_provider_id → auth_provider, каде auth_provider_id претставува идентификатор на независен AuthProvider ентитет и затоа AuthProvider мора да се извлече во посебна релација. Stock атрибутите се целосно независни од User и Portfolio атрибутите и мора да се извлечат како посебен ентитет. '''Зависности кои го предизвикуваат проблемот:''' * Транзитивна зависност: user_id → portfolio_id → balance * FD14: auth_provider_id → auth_provider * 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 зачувани. ✓ === Чекор 3.4: Декомпозиција по FD14 (auth_provider_id → auth_provider) === {{{ AuthProvider(auth_provider_id, auth_provider) PK: auth_provider_id Кандидат клучеви: {auth_provider_id}, {auth_provider} Universal_Relation_Base_10 = Universal_Relation_Base_9 - {auth_provider_id, auth_provider} = ∅ }}} '''FD во AuthProvider:''' FD14: auth_provider_id → auth_provider '''Кандидат клучеви:''' {auth_provider_id}, {auth_provider} '''Нормална форма на AuthProvider:''' 3NF - auth_provider_id е примарен клуч, а auth_provider е кандидат клуч. ✓ '''Lossless join:''' Заедничкиот атрибут auth_provider_id е PK во AuthProvider - lossless. ✓ '''Dependency preservation:''' FD14 е зачувана. ✓ Universal_Relation_Base_10 е празна (∅) - сите атрибути се успешно распоредени. === Проверка за транзитивни зависности во сите релации === ||= Релација =||= Проверка =||= 3NF? =|| || 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 - сите се кандидат клучеви → без транзитивност || ✓ ДА || || AuthProvider || auth_provider_id, auth_provider - сите се кандидат клучеви → без транзитивност || ✓ ДА || '''Заклучок:''' Сите 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 ✓''' ==== AuthProvider - Кандидат клучеви: {auth_provider_id}, {auth_provider} ==== ||= FD =||= Детерминант =||= Суперклуч? =|| || auth_provider_id → auth_provider || auth_provider_id || ✓ ДА || || auth_provider → auth_provider_id || auth_provider || ✓ ДА || '''AuthProvider е во BCNF ✓''' ==== Останати релации ==== 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} - Надворешни клучеви: - AuthProvider(auth_provider_id, auth_provider) - Примарен клуч: auth_provider_id - Кандидат клучеви: {auth_provider_id}, {auth_provider} - Надворешни клучеви: - 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_id, portfolio_id) - Примарен клуч: trade_id - Кандидат клучеви: {trade_id} - Надворешни клучеви: portfolio_id → Portfolios, trade_stock_id → Stock - Напомена: user_id е изводливо преку portfolio_id → Portfolios → Users Transactions(transaction_id, txn_type, txn_quantity, txn_price, txn_timestamp, txn_origin, user_id, stock_id, trade_request_id) - Примарен клуч: transaction_id - Кандидат клучеви: {transaction_id} - Надворешни клучеви: user_id → Users, stock_id → Stock, trade_request_id → Trade_Request 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) НЕ е уникатен - еден корисник може да има повеќе alert-ови за иста акција OAuth_Pending_Links(oauth_token, oauth_email, oauth_provider, oauth_expires_at, oauth_created_at, user_id, oauth_auth_provider_id) - Примарен клуч: oauth_token - Кандидат клучеви: {oauth_token} - Надворешни клучеви: user_id → Users, oauth_auth_provider_id → AuthProvider }}} === Дискусија === ==== Клучни наоди ==== 1. '''Процесот на нормализација го потврди дизајнот''' - декомпозицијата базирана на функционалните зависности резултираше со релации кои одговараат на ентитетите од оригиналниот ER модел од Фаза P2. 2. '''Декомпозицијата е lossless''' - на секој чекор оригиналната информација може да се реконструира преку JOIN операции на заедничките атрибути (надворешни клучеви). Ова е гарантирано со Heath теоремата, бидејќи секоја декомпозиција се врши по FD чиј детерминант е клуч во новата релација. 3. '''Сите функционални зависности се зачувани''' - сите 14 функционални зависности (FD1-FD14) се зачувани во соодветните релации, без потреба за скапи JOIN операции при проверка на ограничувањата. ==== Кој дизајн ќе се користи понатаму ==== Финалниот нормализиран дизајн (Фаза P5) ќе се користи во сите понатамошни фази на проектот. Тој е идентичен со дизајнот од Фаза P2 со следните конкретни измени во имплементацијата: * Додавање на UNIQUE ограничувачи на stock.symbol * Додавање на релација помеѓу Trade_Request и Stock преку stock_id * Додавање на релација помеѓу Transactions и Trade_Request преку trade_request_id * Додавање на релација помеѓу OAuth_Pending_Links и AuthProvider преку auth_provider_id