wiki:Normalization

Version 7 (modified by 231020, 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) само десно
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.
  1. Декомпозицијата е lossless - на секој чекор оригиналната информација може да се реконструира преку JOIN операции на заедничките атрибути (надворешни клучеви). Ова е гарантирано со Heath теоремата, бидејќи секоја декомпозиција се врши по FD чиј детерминант е клуч во новата релација.
  1. Сите функционални зависности се зачувани - сите 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
Note: See TracWiki for help on using the wiki.