wiki:normalization

Version 5 (modified by 231020, 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) само десно
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.
  1. Декомпозицијата е lossless - на секој чекор оригиналната информација може да се реконструира преку JOIN операции на заедничките атрибути (надворешни клучеви). Ова е гарантирано со Heath теоремата, бидејќи секоја декомпозиција се врши по FD чиј детерминант е клуч во новата релација.
  1. Сите функционални зависности се зачувани - сите 13 функционални зависности (FD1-FD13) се зачувани во соодветните релации, без потреба за скапи JOIN операции при проверка на ограничувањата.

Кој дизајн ќе се користи понатаму

Финалниот нормализиран дизајн (Фаза P5) ќе се користи во сите понатамошни фази на проектот. Тој е идентичен со дизајнот од Фаза P2 со следните конкретни измени во имплементацијата:

  • Додавање на UNIQUE ограничувачи на stock.symbol
Note: See TracWiki for help on using the wiki.