| Version 5 (modified by , 3 hours ago) ( diff ) |
|---|
Нормализација - 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) | само десно |
| ✓ (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 | ✓ ДА |
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
Дискусија
Клучни наоди
- Процесот на нормализација го потврди дизајнот - декомпозицијата базирана на функционалните зависности резултираше со релации кои одговараат на ентитетите од оригиналниот ER модел од Фаза P2.
- Декомпозицијата е lossless - на секој чекор оригиналната информација може да се реконструира преку JOIN операции на заедничките атрибути (надворешни клучеви). Ова е гарантирано со Heath теоремата, бидејќи секоја декомпозиција се врши по FD чиј детерминант е клуч во новата релација.
- Сите функционални зависности се зачувани - сите 13 функционални зависности (FD1-FD13) се зачувани во соодветните релации, без потреба за скапи JOIN операции при проверка на ограничувањата.
Кој дизајн ќе се користи понатаму
Финалниот нормализиран дизајн (Фаза P5) ќе се користи во сите понатамошни фази на проектот. Тој е идентичен со дизајнот од Фаза P2 со следните конкретни измени во имплементацијата:
- Додавање на UNIQUE ограничувачи на stock.symbol
