| | 194 | {{{ |
| | 195 | User_Auth_Providers(user_id, auth_provider) |
| | 196 | PK: {user_id, auth_provider} |
| | 197 | FK: user_id → Users |
| | 198 | |
| | 199 | Universal_Relation_Base = Universal_Relation_TradingMK - {auth_provider} |
| | 200 | PK: {holding_id, history_id, trade_id, transaction_id, watchlist_id, oauth_token} |
| | 201 | }}} |
| | 202 | |
| | 203 | '''Функционални зависности во User_Auth_Providers:''' нема FD освен дефиницијата на PK. |
| | 204 | |
| | 205 | '''Функционални зависности во Universal_Relation_Base:''' FD1 – FD13 (auth_provider отстранет). |
| | 206 | |
| | 207 | '''Кандидат клучеви во Universal_Relation_Base:''' {holding_id, history_id, trade_id, transaction_id, watchlist_id, oauth_token} |
| | 208 | |
| | 209 | '''Проверка за lossless join:''' Декомпозицијата е lossless бидејќи заедничкиот атрибут user_id е клуч во Users и FK во User_Auth_Providers — секој auth_provider запис може недвосмислено да се поврзе со корисник. ✓ |
| | 210 | |
| | 211 | '''Проверка за dependency preservation:''' Нема FD која ги поврзува само auth_provider атрибутите — релацијата е дефинирана преку бизнис логика (1:N врска). Сите 13 FD остануваат во Universal_Relation_Base. ✓ |
| | 212 | |
| | 213 | '''Заклучок:''' Universal_Relation_Base е во 1NF. Продолжуваме со 2NF декомпозиција. |
| | 214 | |
| | 215 | ---- |
| | 216 | == 2NF Декомпозиција == |
| | 217 | |
| | 218 | === Дефиниција === |
| | 219 | |
| | 220 | Релацијата е во 2NF ако е во 1NF и секој не-клучен атрибут целосно функционално зависи од целиот примарен клуч - нема парцијални зависности. |
| | 221 | |
| | 222 | === Анализа === |
| | 223 | |
| | 224 | '''Анализирана релација:''' Universal_Relation_Base |
| | 225 | |
| | 226 | '''Примарен клуч:''' {holding_id, history_id, trade_id, transaction_id, watchlist_id, oauth_token} |
| | 227 | |
| | 228 | '''Нормална форма:''' НЕ е во 2NF. Следните FDs претставуваат парцијални зависности: |
| | 229 | |
| | 230 | ||= FD =||= Детерминант =||= Дел од PK? =||= Парцијална зависност? =|| |
| | 231 | || FD8: holding_id → holding_quantity, avg_price, portfolio_id, stock_id || holding_id || ДА || '''ДА''' || |
| | 232 | || FD9: history_id → history_price, history_timestamp, stock_id || history_id || ДА || '''ДА''' || |
| | 233 | || FD10: trade_id → trade_type, trade_status, trade_quantity, price_per_unit, trade_timestamp, trade_stock_symbol, portfolio_id || trade_id || ДА || '''ДА''' || |
| | 234 | || FD11: transaction_id → txn_type, txn_quantity, txn_price, txn_timestamp, txn_origin, user_id, stock_id || transaction_id || ДА || '''ДА''' || |
| | 235 | || FD12: watchlist_id → price_above, price_below, user_id, stock_id || watchlist_id || ДА || '''ДА''' || |
| | 236 | || FD13: oauth_token → oauth_email, oauth_provider, oauth_expires_at, oauth_created_at, user_id || oauth_token || ДА || '''ДА''' || |
| | 237 | || FD1-FD7: транзитивни преку горните детерминанти || не директно || НЕ || Не директно || |
| | 238 | |
| | 239 | Ги елиминираме по ред, почнувајќи со FD8. |
| | 240 | |
| | 241 | === Чекор 2.1: Декомпозиција по FD8 (holding_id → ...) === |
| | 242 | |
| | 243 | '''Нарушува 2NF:''' holding_id е дел од PK, а holding_quantity, avg_price, portfolio_id, stock_id зависат само од него. |
| | 244 | |
| | 245 | {{{ |
| | 246 | Portfolio_Holdings(holding_id, holding_quantity, avg_price, portfolio_id, stock_id) |
| | 247 | PK: holding_id |
| | 248 | FK: portfolio_id → Portfolios, stock_id → Stock |
| | 249 | |
| | 250 | Universal_Relation_Base_1 = Universal_Relation_Base - {holding_id, holding_quantity, avg_price} |
| | 251 | PK: {history_id, trade_id, transaction_id, watchlist_id, oauth_token} |
| | 252 | }}} |
| | 253 | |
| | 254 | '''FD во Portfolio_Holdings:''' FD8: holding_id → holding_quantity, avg_price, portfolio_id, stock_id |
| | 255 | |
| | 256 | '''Кандидат клучеви:''' {holding_id} |
| | 257 | |
| | 258 | '''Напомена:''' (portfolio_id, stock_id) НЕ е уникатен - еден портфолио може да чува исти акции во повеќе лотови. |
| | 259 | |
| | 260 | '''Lossless join:''' Заедничкиот атрибут holding_id е PK во новата релација - lossless. ✓ |
| | 261 | |
| | 262 | '''Dependency preservation:''' FD8 е зачувана во Portfolio_Holdings. ✓ |
| | 263 | |
| | 264 | === Чекор 2.2: Декомпозиција по FD9 (history_id → ...) === |
| | 265 | |
| | 266 | '''Нарушува 2NF:''' history_id е дел од PK, а history_price, history_timestamp, stock_id зависат само од него. |
| | 267 | |
| | 268 | {{{ |
| | 269 | Stock_History(history_id, history_price, history_timestamp, stock_id) |
| | 270 | PK: history_id |
| | 271 | FK: stock_id → Stock |
| | 272 | |
| | 273 | Universal_Relation_Base_2 = Universal_Relation_Base_1 - {history_id, history_price, history_timestamp} |
| | 274 | PK: {trade_id, transaction_id, watchlist_id, oauth_token} |
| | 275 | }}} |
| | 276 | |
| | 277 | '''FD во Stock_History:''' FD9: history_id → history_price, history_timestamp, stock_id |
| | 278 | |
| | 279 | '''Кандидат клучеви:''' {history_id} |
| | 280 | |
| | 281 | '''Lossless join:''' Заедничкиот атрибут history_id е PK - lossless. ✓ |
| | 282 | |
| | 283 | '''Dependency preservation:''' FD9 е зачувана во Stock_History. ✓ |
| | 284 | |
| | 285 | === Чекор 2.3: Декомпозиција по FD10 (trade_id → ...) === |
| | 286 | |
| | 287 | '''Нарушува 2NF:''' trade_id е дел од PK, а trade_type, trade_status, trade_quantity, price_per_unit, trade_timestamp, trade_stock_symbol, portfolio_id зависат само од него. |
| | 288 | |
| | 289 | {{{ |
| | 290 | Trade_Request(trade_id, trade_type, trade_status, trade_quantity, price_per_unit, |
| | 291 | trade_timestamp, trade_stock_symbol, portfolio_id) |
| | 292 | PK: trade_id |
| | 293 | FK: portfolio_id → Portfolios |
| | 294 | |
| | 295 | Universal_Relation_Base_3 = Universal_Relation_Base_2 - {trade_id, trade_type, trade_status, |
| | 296 | trade_quantity, price_per_unit, trade_timestamp, trade_stock_symbol} |
| | 297 | PK: {transaction_id, watchlist_id, oauth_token} |
| | 298 | }}} |
| | 299 | |
| | 300 | '''FD во Trade_Request:''' FD10: trade_id → trade_type, trade_status, trade_quantity, price_per_unit, trade_timestamp, trade_stock_symbol, portfolio_id |
| | 301 | |
| | 302 | '''Кандидат клучеви:''' {trade_id} |
| | 303 | |
| | 304 | '''Напомена:''' user_id НЕ е складиран во Trade_Request бидејќи е изводливо преку portfolio_id → Portfolios → Users, елиминирајќи ја редундантноста. |
| | 305 | |
| | 306 | '''Lossless join:''' Заедничкиот атрибут trade_id е PK - lossless. ✓ |
| | 307 | |
| | 308 | '''Dependency preservation:''' FD10 е зачувана во Trade_Request. ✓ |
| | 309 | |
| | 310 | === Чекор 2.4: Декомпозиција по FD11 (transaction_id → ...) === |
| | 311 | |
| | 312 | '''Нарушува 2NF:''' transaction_id е дел од PK, а txn_type, txn_quantity, txn_price, txn_timestamp, txn_origin, user_id, stock_id зависат само од него. |
| | 313 | |
| | 314 | {{{ |
| | 315 | Transactions(transaction_id, txn_type, txn_quantity, txn_price, txn_timestamp, |
| | 316 | txn_origin, user_id, stock_id) |
| | 317 | PK: transaction_id |
| | 318 | FK: user_id → Users, stock_id → Stock |
| | 319 | |
| | 320 | Universal_Relation_Base_4 = Universal_Relation_Base_3 - {transaction_id, txn_type, txn_quantity, |
| | 321 | txn_price, txn_timestamp, txn_origin} |
| | 322 | PK: {watchlist_id, oauth_token} |
| | 323 | }}} |
| | 324 | |
| | 325 | '''FD во Transactions:''' FD11: transaction_id → txn_type, txn_quantity, txn_price, txn_timestamp, txn_origin, user_id, stock_id |
| | 326 | |
| | 327 | '''Кандидат клучеви:''' {transaction_id} |
| | 328 | |
| | 329 | '''Lossless join:''' Заедничкиот атрибут transaction_id е PK - lossless. ✓ |
| | 330 | |
| | 331 | '''Dependency preservation:''' FD11 е зачувана во Transactions. ✓ |
| | 332 | |
| | 333 | === Чекор 2.5: Декомпозиција по FD12 (watchlist_id → ...) === |
| | 334 | |
| | 335 | '''Нарушува 2NF:''' watchlist_id е дел од PK, а price_above, price_below, user_id, stock_id зависат само од него. |
| | 336 | |
| | 337 | {{{ |
| | 338 | Watchlist(watchlist_id, price_above, price_below, user_id, stock_id) |
| | 339 | PK: watchlist_id |
| | 340 | FK: user_id → Users, stock_id → Stock |
| | 341 | |
| | 342 | Universal_Relation_Base_5 = Universal_Relation_Base_4 - {watchlist_id, price_above, price_below} |
| | 343 | PK: {oauth_token} |
| | 344 | }}} |
| | 345 | |
| | 346 | '''FD во Watchlist:''' FD12: watchlist_id → price_above, price_below, user_id, stock_id |
| | 347 | |
| | 348 | '''Кандидат клучеви:''' {watchlist_id} |
| | 349 | |
| | 350 | '''Напомена:''' (user_id, stock_id) НЕ е уникатен - еден корисник може да има повеќе alert-ови за иста акција. |
| | 351 | |
| | 352 | '''Lossless join:''' Заедничкиот атрибут watchlist_id е PK - lossless. ✓ |
| | 353 | |
| | 354 | '''Dependency preservation:''' FD12 е зачувана во Watchlist. ✓ |
| | 355 | |
| | 356 | === Чекор 2.6: Декомпозиција по FD13 (oauth_token → ...) === |
| | 357 | |
| | 358 | '''Нарушува 2NF:''' oauth_token е дел од PK, а oauth_email, oauth_provider, oauth_expires_at, oauth_created_at, user_id зависат само од него. |
| | 359 | |
| | 360 | {{{ |
| | 361 | OAuth_Pending_Links(oauth_token, oauth_email, oauth_provider, oauth_expires_at, |
| | 362 | oauth_created_at, user_id) |
| | 363 | PK: oauth_token |
| | 364 | FK: user_id → Users |
| | 365 | |
| | 366 | Universal_Relation_Base_6 = Universal_Relation_Base_5 - {oauth_token, oauth_email, |
| | 367 | oauth_provider, oauth_expires_at, oauth_created_at} |
| | 368 | Останати атрибути: |
| | 369 | (user_id, username, password, email, role, |
| | 370 | portfolio_id, balance, |
| | 371 | stock_id, stock_symbol, stock_name, current_price, last_price, percentage, turnover, last_updated) |
| | 372 | PK: ∅ |
| | 373 | }}} |
| | 374 | |
| | 375 | '''FD во OAuth_Pending_Links:''' FD13: oauth_token → oauth_email, oauth_provider, oauth_expires_at, oauth_created_at, user_id |
| | 376 | |
| | 377 | '''Кандидат клучеви:''' {oauth_token} |
| | 378 | |
| | 379 | '''Lossless join:''' Заедничкиот атрибут oauth_token е PK - lossless. ✓ |
| | 380 | |
| | 381 | '''Dependency preservation:''' FD13 е зачувана во OAuth_Pending_Links. ✓ |
| | 382 | |
| | 383 | === Состојба после 2NF декомпозиција === |
| | 384 | |
| | 385 | По извлекувањето на сите парцијални зависности, останатата релација Universal_Relation_Base_6 содржи три независни ентитети: |
| | 386 | |
| | 387 | {{{ |
| | 388 | Universal_Relation_Base_6( |
| | 389 | user_id, username, password, email, role, |
| | 390 | portfolio_id, balance, |
| | 391 | stock_id, stock_symbol, stock_name, current_price, last_price, percentage, turnover, last_updated |
| | 392 | ) |
| | 393 | PK: ∅ |
| | 394 | }}} |
| | 395 | |
| | 396 | Оваа релација веќе нема парцијални зависности, па продолжуваме директно со проверка за 3NF. Меѓутоа, содржи транзитивни зависности - нарушување на 3NF. |
| | 397 | |
| | 398 | {{{ |
| | 399 | Состојба по 2NF: |
| | 400 | |
| | 401 | User_Auth_Providers(user_id, auth_provider) — од 1NF |
| | 402 | Portfolio_Holdings(holding_id, holding_quantity, avg_price, portfolio_id, stock_id) |
| | 403 | Stock_History(history_id, history_price, history_timestamp, stock_id) |
| | 404 | Trade_Request(trade_id, trade_type, trade_status, trade_quantity, price_per_unit, |
| | 405 | trade_timestamp, trade_stock_symbol, portfolio_id) |
| | 406 | Transactions(transaction_id, txn_type, txn_quantity, txn_price, txn_timestamp, |
| | 407 | txn_origin, user_id, stock_id) |
| | 408 | Watchlist(watchlist_id, price_above, price_below, user_id, stock_id) |
| | 409 | OAuth_Pending_Links(oauth_token, oauth_email, oauth_provider, oauth_expires_at, |
| | 410 | oauth_created_at, user_id) |
| | 411 | Universal_Relation_Base_6(user_id, username, password, email, role, |
| | 412 | portfolio_id, balance, |
| | 413 | stock_id, stock_symbol, stock_name, current_price, last_price, |
| | 414 | percentage, turnover, last_updated) PK: ∅ |
| | 415 | }}} |
| | 416 | |
| | 417 | ---- |
| | 418 | |
| | 419 | == 3NF Декомпозиција == |
| | 420 | |
| | 421 | === Дефиниција === |
| | 422 | |
| | 423 | Релацијата е во 3NF ако е во 2NF и за секоја нетривијална FD X → A, или X е суперклуч, или A е примарен атрибут. Ова ги елиминира транзитивните зависности. prov |
| | 424 | |
| | 425 | === Анализа на Universal_Relation_Base_6 === |
| | 426 | |
| | 427 | '''Анализирана релација:''' Universal_Relation_Base_6 |
| | 428 | |
| | 429 | '''Применети FD:''' FD1, FD2, FD3, FD4, FD5, FD6, FD7 |
| | 430 | |
| | 431 | '''Нормална форма:''' НЕ е во 3NF. Постои транзитивна зависност: user_id → portfolio_id → balance (balance е транзитивно зависен од user_id преку portfolio_id). Stock атрибутите се целосно независни од User атрибутите. |
| | 432 | |
| | 433 | '''Зависности кои предизвикуваат проблем:''' |
| | 434 | * Транзитивна зависност: user_id → portfolio_id → balance (balance зависи транзитивно од user_id преку portfolio_id кој не е PK во оваа релација) |
| | 435 | * FD6 и FD7 формираат независен ентитет (Stock) кој не е функционално поврзан со User или Portfolio во оваа релација - мора да се извлече посебно |
| | 436 | |
| | 437 | === Чекор 3.1: Декомпозиција по FD1/FD2/FD3 (user_id → ...) === |
| | 438 | |
| | 439 | {{{ |
| | 440 | Users(user_id, username, password, email, role) |
| | 441 | PK: user_id |
| | 442 | Кандидат клучеви: {user_id}, {username}, {email} |
| | 443 | |
| | 444 | Universal_Relation_Base_7 = Universal_Relation_Base_6 - {username, password, email, role} |
| | 445 | (user_id останува за поврзување со Portfolios) |
| | 446 | }}} |
| | 447 | |
| | 448 | '''FD во Users:''' FD1: user_id → username, password, email, role; FD2: username → user_id; FD3: email → user_id |
| | 449 | |
| | 450 | '''Кандидат клучеви:''' {user_id}, {username}, {email} |
| | 451 | |
| | 452 | '''Нормална форма на Users:''' 3NF - сите детерминанти (user_id, username, email) се кандидат клучеви (суперклучеви). ✓ |
| | 453 | |
| | 454 | '''Lossless join:''' Заедничкиот атрибут user_id е PK во Users - lossless. ✓ |
| | 455 | |
| | 456 | '''Dependency preservation:''' FD1, FD2, FD3 зачувани. ✓ |
| | 457 | |
| | 458 | === Чекор 3.2: Декомпозиција по FD4/FD5 (portfolio_id ↔ user_id) === |
| | 459 | |
| | 460 | {{{ |
| | 461 | Portfolios(portfolio_id, balance, user_id) |
| | 462 | PK: portfolio_id |
| | 463 | Кандидат клучеви: {portfolio_id}, {user_id} (1:1 врска — секој корисник има точно едно портфолио) |
| | 464 | FK: user_id → Users |
| | 465 | |
| | 466 | Universal_Relation_Base_8 = Universal_Relation_Base_7 - {portfolio_id, balance, user_id} |
| | 467 | }}} |
| | 468 | |
| | 469 | '''FD во Portfolios:''' FD4: portfolio_id → balance, user_id; FD5: user_id → portfolio_id |
| | 470 | |
| | 471 | '''Кандидат клучеви:''' {portfolio_id}, {user_id} |
| | 472 | |
| | 473 | '''Нормална форма на Portfolios:''' 3NF — двата детерминанти (portfolio_id и user_id) се кандидат клучеви. ✓ |
| | 474 | |
| | 475 | '''Lossless join:''' Заедничкиот атрибут user_id/portfolio_id — lossless. ✓ |
| | 476 | |
| | 477 | '''Dependency preservation:''' FD4, FD5 зачувани. ✓ |
| | 478 | |
| | 479 | === Чекор 3.3: Декомпозиција по FD6/FD7 (stock_id ↔ stock_symbol) === |
| | 480 | |
| | 481 | {{{ |
| | 482 | Stock(stock_id, stock_symbol, stock_name, current_price, last_price, percentage, turnover, last_updated) |
| | 483 | PK: stock_id |
| | 484 | Кандидат клучеви: {stock_id}, {stock_symbol} |
| | 485 | |
| | 486 | Universal_Relation_Base_9 = Universal_Relation_Base_8 - {stock_id, stock_symbol, stock_name, |
| | 487 | current_price, last_price, percentage, turnover, last_updated} |
| | 488 | = ∅ (сите атрибути се распоредени) |
| | 489 | }}} |
| | 490 | |
| | 491 | '''FD во Stock:''' FD6: stock_id → stock_symbol, stock_name, current_price, last_price, percentage, turnover, last_updated; FD7: stock_symbol → stock_id |
| | 492 | |
| | 493 | '''Кандидат клучеви:''' {stock_id}, {stock_symbol} |
| | 494 | |
| | 495 | '''Нормална форма на Stock:''' 3NF - двата детерминанти (stock_id и stock_symbol) се кандидат клучеви. ✓ |
| | 496 | |
| | 497 | '''Lossless join:''' Заедничкиот атрибут stock_id - lossless. |
| | 498 | |
| | 499 | '''Dependency preservation:''' FD6, FD7 зачувани. ✓ |
| | 500 | |
| | 501 | Universal_Relation_Base_9 е празна (∅) - сите атрибути се успешно распоредени. |
| | 502 | |
| | 503 | === Проверка за транзитивни зависности во сите релации === |
| | 504 | |
| | 505 | ||= Релација =||= Проверка =||= 3NF? =|| |
| | 506 | || User_Auth_Providers || Нема не-клучни атрибути - само PK || ✓ ДА || |
| | 507 | || Portfolio_Holdings || holding_id → сите; portfolio_id и stock_id не одредуваат меѓусебно никој не-клучен атрибут || ✓ ДА || |
| | 508 | || Stock_History || history_id → сите; stock_id не одредува history_price/timestamp во оваа релација || ✓ ДА || |
| | 509 | || Trade_Request || trade_id → сите; нема транзитивни зависности || ✓ ДА || |
| | 510 | || Transactions || transaction_id → сите; нема транзитивни зависности || ✓ ДА || |
| | 511 | || Watchlist || watchlist_id → сите; нема транзитивни зависности || ✓ ДА || |
| | 512 | || OAuth_Pending_Links || oauth_token → сите; нема транзитивни зависности || ✓ ДА || |
| | 513 | || Users || user_id, username, email - сите се кандидат клучеви → без транзитивност || ✓ ДА || |
| | 514 | || Portfolios || portfolio_id, user_id - сите се кандидат клучеви → без транзитивност || ✓ ДА || |
| | 515 | || Stock || stock_id, stock_symbol - сите се кандидат клучеви → без транзитивност || ✓ ДА || |
| | 516 | |
| | 517 | '''Заклучок:''' Сите 10 релации се во 3NF. ✓ |
| | 518 | |
| | 519 | ---- |
| | 520 | |
| | 521 | == BCNF Декомпозиција == |
| | 522 | |
| | 523 | === Дефиниција === |
| | 524 | |
| | 525 | Релацијата е во BCNF ако за секоја нетривијална FD X → Y, X е суперклуч. BCNF е построга од 3NF; нарушувања можат да се појават само кај релации со повеќе преклопувачки кандидат клучеви. |
| | 526 | |
| | 527 | === Проверка за BCNF === |
| | 528 | |
| | 529 | Ги проверуваме само релациите со повеќе кандидат клучеви: |
| | 530 | |
| | 531 | ==== Users - Кандидат клучеви: {user_id}, {username}, {email} ==== |
| | 532 | |
| | 533 | ||= FD =||= Детерминант =||= Суперклуч? =|| |
| | 534 | || user_id → username, password, email, role || user_id || ✓ ДА || |
| | 535 | || username → user_id || username || ✓ ДА || |
| | 536 | || email → user_id || email || ✓ ДА || |
| | 537 | |
| | 538 | '''Users е во BCNF ✓''' |
| | 539 | |
| | 540 | ==== Portfolios - Кандидат клучеви: {portfolio_id}, {user_id} ==== |
| | 541 | |
| | 542 | ||= FD =||= Детерминант =||= Суперклуч? =|| |
| | 543 | || portfolio_id → balance, user_id || portfolio_id || ✓ ДА || |
| | 544 | || user_id → portfolio_id, balance || user_id || ✓ ДА || |
| | 545 | |
| | 546 | '''Portfolios е во BCNF ✓''' |
| | 547 | |
| | 548 | ==== Stock - Кандидат клучеви: {stock_id}, {stock_symbol} ==== |
| | 549 | |
| | 550 | ||= FD =||= Детерминант =||= Суперклуч? =|| |
| | 551 | || stock_id → сите атрибути || stock_id || ✓ ДА || |
| | 552 | || stock_symbol → stock_id и транзитивно сите || stock_symbol || ✓ ДА || |
| | 553 | |
| | 554 | '''Stock е во BCNF ✓''' |
| | 555 | |
| | 556 | ==== Останати 7 релации ==== |
| | 557 | |
| | 558 | User_Auth_Providers, Portfolio_Holdings, Stock_History, Trade_Request, Transactions, Watchlist, OAuth_Pending_Links - секоја има точно еден кандидат клуч. Единствената нетривијална FD го има PK-от како детерминант. |
| | 559 | |
| | 560 | '''Сите се во BCNF ✓''' |
| | 561 | |
| | 562 | '''Заклучок: Сите 10 релации се во BCNF - највисоката нормална форма постигната. ✓''' |
| | 563 | |
| | 564 | ---- |
| | 565 | |
| | 566 | == Финален резултат и дискусија == |
| | 567 | |
| | 568 | === Нормализиран релационен модел === |
| | 569 | |
| | 570 | {{{ |
| | 571 | Users(user_id, username, password, email, role) |
| | 572 | - Примарен клуч: user_id |
| | 573 | - Кандидат клучеви: {user_id}, {username}, {email} |
| | 574 | - Надворешни клучеви: - |
| | 575 | |
| | 576 | User_Auth_Providers(user_id, auth_provider) |
| | 577 | - Примарен клуч: {user_id, auth_provider} |
| | 578 | - Кандидат клучеви: {user_id, auth_provider} |
| | 579 | - Надворешни клучеви: user_id → Users |
| | 580 | |
| | 581 | Portfolios(portfolio_id, balance, user_id) |
| | 582 | - Примарен клуч: portfolio_id |
| | 583 | - Кандидат клучеви: {portfolio_id}, {user_id} |
| | 584 | - Надворешни клучеви: user_id → Users |
| | 585 | - Напомена: 1:1 врска со Users - секој корисник има точно едно портфолио |
| | 586 | |
| | 587 | Stock(stock_id, stock_symbol, stock_name, current_price, last_price, percentage, turnover, last_updated) |
| | 588 | - Примарен клуч: stock_id |
| | 589 | - Кандидат клучеви: {stock_id}, {stock_symbol} |
| | 590 | - Надворешни клучеви: — |
| | 591 | |
| | 592 | Portfolio_Holdings(holding_id, holding_quantity, avg_price, portfolio_id, stock_id) |
| | 593 | - Примарен клуч: holding_id |
| | 594 | - Кандидат клучеви: {holding_id} |
| | 595 | - Надворешни клучеви: portfolio_id → Portfolios, stock_id → Stock |
| | 596 | - Напомена: (portfolio_id, stock_id) НЕ е уникатен - еден портфолио може да чува |
| | 597 | исти акции во повеќе места |
| | 598 | |
| | 599 | Stock_History(history_id, history_price, history_timestamp, stock_id) |
| | 600 | - Примарен клуч: history_id |
| | 601 | - Кандидат клучеви: {history_id} |
| | 602 | - Надворешни клучеви: stock_id → Stock |
| | 603 | |
| | 604 | Trade_Request(trade_id, trade_type, trade_status, trade_quantity, price_per_unit, |
| | 605 | trade_timestamp, trade_stock_symbol, portfolio_id) |
| | 606 | - Примарен клуч: trade_id |
| | 607 | - Кандидат клучеви: {trade_id} |
| | 608 | - Надворешни клучеви: portfolio_id → Portfolios |
| | 609 | - Напомена: user_id е изводливо преку portfolio_id → Portfolios → Users |
| | 610 | |
| | 611 | Transactions(transaction_id, txn_type, txn_quantity, txn_price, txn_timestamp, |
| | 612 | txn_origin, user_id, stock_id) |
| | 613 | - Примарен клуч: transaction_id |
| | 614 | - Кандидат клучеви: {transaction_id} |
| | 615 | - Надворешни клучеви: user_id → Users, stock_id → Stock |
| | 616 | |
| | 617 | Watchlist(watchlist_id, price_above, price_below, user_id, stock_id) |
| | 618 | - Примарен клуч: watchlist_id |
| | 619 | - Кандидат клучеви: {watchlist_id} |
| | 620 | - Надворешни клучеви: user_id → Users, stock_id → Stock |
| | 621 | - Напомена: (user_id, stock_id) НЕ е уникатен - еден корисник може да има |
| | 622 | повеќе alerт-ови за иста акција |
| | 623 | |
| | 624 | OAuth_Pending_Links(oauth_token, oauth_email, oauth_provider, oauth_expires_at, |
| | 625 | oauth_created_at, user_id) |
| | 626 | - Примарен клуч: oauth_token |
| | 627 | - Кандидат клучеви: {oauth_token} |
| | 628 | - Надворешни клучеви: user_id → Users |
| | 629 | }}} |
| | 630 | |
| | 631 | === Дискусија === |
| | 632 | |
| | 633 | ==== Клучни наоди ==== |
| | 634 | |
| | 635 | 1. '''Процесот на нормализација го потврди дизајнот''' - декомпозицијата базирана на функционалните зависности резултираше со релации кои одговараат на ентитетите од оригиналниот ER модел од Фаза P2. |
| | 636 | |
| | 637 | 2. '''Декомпозицијата е lossless''' - на секој чекор оригиналната информација може да се реконструира преку JOIN операции на заедничките атрибути (надворешни клучеви). Ова е гарантирано со Heath теоремата, бидејќи секоја декомпозиција се врши по FD чиј детерминант е клуч во новата релација. |
| | 638 | |
| | 639 | 3. '''Сите функционални зависности се зачувани''' - сите 13 функционални зависности (FD1-FD13) се зачувани во соодветните релации, без потреба за скапи JOIN операции при проверка на ограничувањата. |
| | 640 | |
| | 641 | |
| | 642 | ==== Кој дизајн ќе се користи понатаму ==== |
| | 643 | |
| | 644 | Финалниот нормализиран дизајн (Фаза P5) ќе се користи во сите понатамошни фази на проектот. Тој е идентичен со дизајнот од Фаза P2 со следните конкретни измени во имплементацијата: |
| | 645 | * Додавање на UNIQUE ограничувачи на stock.symbol |
| | 646 | |