Ignore:
Timestamp:
09/24/26 17:43:19 (5 days ago)
Author:
Stefan <trsunovstefan@…>
Branches:
main
Children:
0cee8ec
Parents:
a531b45
Message:

Wiki docs, phase 6 and phase 7 added

File:
1 edited

Legend:

Unmodified
Added
Removed
  • docs/P5-Normalization/Normalization.md

    ra531b45 ref1c1c7  
    257257again: nothing was lost.
    258258
    259 **Lossless join.** For every pair (referencing relation, referenced relation) connected by a
    260 foreign key — `R_MARKETS.M_CRYPTO_ID → R_CRYPTO.C_ID`, `R_HOLDINGS.H_USER_ID → R_USERS.U_ID` /
    261 `R_HOLDINGS.H_CRYPTO_ID → R_CRYPTO.C_ID`, `R_ORDERS.O_USER_ID → R_USERS.U_ID` /
    262 `R_ORDERS.O_MARKET_ID → R_MARKETS.M_ID`, `R_TRANSACTIONS.T_USER_ID → R_USERS.U_ID` /
    263 `R_TRANSACTIONS.T_RELATED_ORDER → R_ORDERS.O_ID`, `R_MARKET_TRADES.MT_MARKET_ID →
    264 R_MARKETS.M_ID`, `R_MARKET_CANDLES.MC_MARKET_ID → R_MARKETS.M_ID`,
    265 `R_WATCHLISTS.W_USER_ID → R_USERS.U_ID`, `R_WATCHLIST_ITEMS.WI_WATCHLIST_ID →
    266 R_WATCHLISTS.W_ID` / `R_WATCHLIST_ITEMS.WI_CRYPTO_ID → R_CRYPTO.C_ID` — the join attribute on
    267 the "one" side is that relation's own primary key (`U_ID`, `C_ID`, `M_ID`, `O_ID`, `W_ID`).
    268 A join on a foreign key equated to the primary key it references is the textbook sufficient
    269 condition for a lossless decomposition (`Ri ∩ Rj` is a key of `Rj`), so re-joining all ten
    270 relations on their foreign-key/primary-key pairs reconstructs `R_EDUBERZA` exactly, with no
    271 spurious rows and none missing.
     259**Lossless join — chase test.**
     260
     261> *Note: the chase algorithm is not part of the course material. I was curious about a
     262> stricter way to test lossless join than the usual "the common attributes are a key of one
     263> side" argument, so I applied it here.*
     264
     265The chase decides whether a decomposition `R = R1 ∪ … ∪ Rn` is lossless under a set of
     266functional dependencies. Build a tableau with one column per attribute of `R` and one row per
     267relation `Ri`. In row `i`, put a distinguished symbol `a` in every column of `Ri` and a unique
     268symbol `b_i` in every other column. Then repeat, until nothing changes: for each FD `X → Y`,
     269whenever two rows agree on all of `X`, make them agree on `Y`. If they disagree, an `a` wins,
     270otherwise one `b` replaces the other. **The decomposition is lossless exactly when some row
     271ends up with `a` in every column.**
     272
     273All attributes of one cluster (`U_*`, `C_*`, `M_*`, …) always appear together, and FD1–FD17
     274never mix clusters. So each cluster is one column group below: `a` means every column of the
     275group holds a distinguished symbol, and `b` means none of them does. The foreign-key
     276attributes (`H_USER_ID`, `O_MARKET_ID`, …) belong to their own cluster (`H_*`, `O_*`, …), not
     277to the cluster they reference.
     278
     279**Step 1 — the ten relations from the table above.**
     280
     281```
     282              U*  C*  M*  H*  O*  T*  MT* MC* W*  WI*
     283R_USERS        a   b   b   b   b   b   b   b   b   b
     284R_CRYPTO       b   a   b   b   b   b   b   b   b   b
     285R_MARKETS      b   b   a   b   b   b   b   b   b   b
     286R_HOLDINGS     b   b   b   a   b   b   b   b   b   b
     287R_ORDERS       b   b   b   b   a   b   b   b   b   b
     288R_TRANSACTIONS b   b   b   b   b   a   b   b   b   b
     289R_MARKET_TR.   b   b   b   b   b   b   a   b   b   b
     290R_MARKET_CA.   b   b   b   b   b   b   b   a   b   b
     291R_WATCHLISTS   b   b   b   b   b   b   b   b   a   b
     292R_WATCHLIST_I. b   b   b   b   b   b   b   b   b   a
     293```
     294
     295Every FD has its left side inside one cluster, for example `U_ID → U_*` or
     296`H_USER_ID, H_CRYPTO_ID → H_ID`. For such an FD to fire, two rows would have to agree on that
     297left side. But only one row has `a`s in that cluster, and the `b`s of different rows are
     298all different, so no two rows ever agree on any left side. **The chase changes nothing, and
     299no row becomes all `a`.** Under FD1–FD17 alone, the ten relations are *not* guaranteed to
     300join back to `R_EDUBERZA`. This is not an accident of this model. It is exactly why
     301Bernstein's synthesis algorithm has a final step: *if no synthesised relation contains a
     302candidate key of `R`, add one that does.* None of the ten contains the ten-attribute key.
     303
     304**Step 2 — add the key relation** `R_KEY(U_ID, C_ID, M_ID, H_ID, O_ID, T_ID, MT_ID, MC_ID,
     305W_ID, WI_ID)`. Its row has `a` only in the ten ID columns, written `a·` for "`a` in the ID,
     306`b` in the rest of the group":
     307
     308```
     309              U*  C*  M*  H*  O*  T*  MT* MC* W*  WI*
     310R_KEY          a·  a·  a·  a·  a·  a·  a·  a·  a·  a·
     311(the ten rows of step 1 unchanged)
     312```
     313
     314Now FD1 `U_ID → U_*` fires: row `R_KEY` and row `R_USERS` both have `a` in `U_ID`, so they
     315must agree on the rest of `U_*`, and `R_USERS` has `a` there. `R_KEY` becomes `a` in the whole
     316`U*` group. The same happens with FD4 (`C*`), FD6 (`M*`), FD8 (`H*`), FD10 (`O*`), FD11 (`T*`),
     317FD12 (`MT*`), FD13 (`MC*`), FD15 (`W*`) and FD16 (`WI*`):
     318
     319```
     320              U*  C*  M*  H*  O*  T*  MT* MC* W*  WI*
     321R_KEY          a   a   a   a   a   a   a   a   a   a     <- all distinguished
     322```
     323
     324**Row `R_KEY` is all `a`, so the decomposition into the ten relations plus `R_KEY` is
     325lossless.**
     326
     327**Why `R_KEY` is not kept in the final schema.** An instance of `R_KEY` would only record
     328which ID of one cluster appears together with which ID of every other cluster. As shown under
     329*Candidate keys and primary key*, the ten clusters are independent record types, and
     330`R_EDUBERZA` pairs every row of one with every row of the others. So `R_KEY` would be just the
     331cross product of the ten ID sets and would carry no information. The same independence means
     332the join dependency `⋈[R_USERS, …, R_WATCHLIST_ITEMS]` holds on `R_EDUBERZA` by construction.
     333Under that dependency the ten relations alone already reconstruct it: their natural join, with
     334no common attributes, is exactly that cross product. The chase makes this reasoning explicit.
     335FDs by themselves cannot prove the join lossless; you need either the key relation or the
     336independence of the clusters. That was hidden in the earlier "foreign key equals primary key"
     337argument, which described the equi-joins the application runs, not the natural join the
     338lossless-join property is about.
    272339
    273340## 3NF decomposition
Note: See TracChangeset for help on using the changeset viewer.