| Version 7 (modified by , 2 weeks ago) ( diff ) |
|---|
Нормализација - 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) | само десно |
| ✓ (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 | ✓ ДА |
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
Дискусија
Клучни наоди
- Процесот на нормализација го потврди дизајнот - декомпозицијата базирана на функционалните зависности резултираше со релации кои одговараат на ентитетите од оригиналниот ER модел од Фаза P2.
- Декомпозицијата е lossless - на секој чекор оригиналната информација може да се реконструира преку JOIN операции на заедничките атрибути (надворешни клучеви). Ова е гарантирано со Heath теоремата, бидејќи секоја декомпозиција се врши по FD чиј детерминант е клуч во новата релација.
- Сите функционални зависности се зачувани - сите 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
