Context:FIT3003_MOC Β· Building Dimension Tables assumes a memberβs attributes never move β SCD is the answer when they do Β· (Chapter 6; verified against the W12 revision chapter)Parent Framework:Star Schema
Quick Revision
π― Objective: fold the temporal (historical) aspect of a dimension record into the warehouse β a measure must be reportable against the attribute value that was true at the time of the fact, not the value true today.
π¦ Core Components:Types 0 / 1 β no history at all | Types 2 / 3 / 6 β history kept inside the dimension | Type 4 β history split into a separate table (a bridge / weak entity).
β‘ Key Constraint: overwriting a changed attribute silently re-prices the past β every historic fact row is then reported at the current value.
π How It Works
1. The failure it fixes β Bookshop case study
Temporal attribute β an attribute whose value has a life-time (C1βs price \45.95isvalidonlyJan2007βJul2007)β ββtemporaldimensionβββadimensionwhoseββrecordββhasalifeβtimeβ thelifeβtimeisthe(\text{StartDate}, \text{EndDate})$ pair.
βSlowlyβ β for records that change slowly over time (a bookβs price) β not applicable to rapidly changing data Β· original types 1, 2, 3; newer types 0, 4, 6.
Operational source β BOOK(BookIDβ,BookTitle,Author) joined to BOOKTRANSACTION, with price history held apart in BOOKPRICEHISTORY(BookIDβ,StartDate,EndDateβ,Price,Remarks).
The naive star β BookSalesFACT(TimeIDβ,BranchIDβ,BookIDββ,Number_of_Books_Sold) with the currentPrice stored flat inside BookDIM.
Symptom β CSIRO Diet (C1) sells far more in Dec2007, yet the report shows \45.95everymonth;βHarryPotter6βreports$30.95inMar2008thoughitsoldat$10.00$ β arithmetically correct, commercially wrong.
C1βs real history β Jan2007βJul2007 \45.95fullβ Aug2007βOct2007$36.7520%offβ Nov2007βJan2008$23.00ββhalfpriceβββ Feb2008βDec9999$45.95$ full β the Dec2007 spike was the discount.
Open-ended sentinel β the current price row carries EndDate=Dec9999, so βthe price nowβ is a range test, not a max().
After the fix β a second measure Total_Sales can be added to BookSalesFACT once the price valid at sale time is reachable.
2. The type ladder
Type 0 β original β stores the initial value of the record and never touches it again.
Type 1 β latest β overwrites with the latest value (H6 β \10.00$). Shared weakness of 0 and 1 β neither records the history of changes at all.
Type 2 β versioned rows β history is not separated from the main dimension; new records keep being added: BookDIM gains StartDate,EndDate,Price,Remarks,CurrentFlag and a sequence number is appended to the original BookID (C1_1 β¦ C1_4, H6_1 β¦ H6_3, DV_1) β each version is its own member, and the factβs FK points at the version: Dec2007 sales carry C1_3, Mar2008 sales C1_4.
Type 3 β previous column β a simplification of Type 2 assuming the complete history is not necessary: one row per member, CurrentPrice beside PreviousPrice (C1 \45.95 / $23.00β H6$10.00 / $30.95β DV$27.95 /$ Null when never changed).
Type 4 β history table β a new dimension holds the full change history; BookDIM keeps only the stable attributes and the price columns move to BookPriceDIM. The same book keeps oneBookID.
Type 4 for a changed business rule(W11) β when Semester 1βs end date moves (15 Jul β 30 Jun from 2021), SemesterHistoryDIM(SemesterIDβ,VersionNoβ,StartYear,EndYear,StartDate,EndDate,Remark) keeps both versions, EndYear null marking the one in force β Active Data Warehousing.
Type 6 β 2 + 3 β no separate identifier for the same book, but the entire history is kept: one row per version, each carrying its own CurrentPrice and the PreviousPrice before it (C1: Null β 45.95 β 36.75 β 23.00): BookDIM(BookID,BookTitle,Author,StartDate,EndDate,CurrentPrice,PreviousPrice,Remarks,CurrentFlag).
3. Type 6 breaks the starβs cardinality
The problem β BookID now repeats across versions, so BookDIM β BookSalesFACT is no longer 1βm but mβm: a fact rowβs BookID matches every version of that book.
Three fixes β β add a new surrogate key to BookDIM (Surrogate Key) β‘ add StartDate,EndDate to the fact, beside BookID β’ add an associative (bridge) table between BookDIM and the fact (Bridge Tables).
βοΈ Core Implementation
πΉ Type 4 β the shape the lecture builds
Mermaid erDiagram β history split out of BookDIM
erDiagram
TIME_DIM ||--o{ BOOK_SALES_FACT : qualifies
BRANCH_DIM ||--o{ BOOK_SALES_FACT : qualifies
BOOK_DIM ||--o{ BOOK_SALES_FACT : qualifies
BOOK_DIM ||--o{ BOOK_PRICE_DIM : "price over time"
BOOK_SALES_FACT {
VARCHAR2 TimeID FK
VARCHAR2 BranchID FK
VARCHAR2 BookID FK
NUMBER Number_of_Books_Sold
}
BOOK_DIM {
VARCHAR2 BookID PK
VARCHAR2 BookTitle
VARCHAR2 Author
}
BOOK_PRICE_DIM {
VARCHAR2 BookID PK
DATE StartDate PK
DATE EndDate PK
NUMBER Price
VARCHAR2 Remarks
}
π‘ Common Mistake:Minting a new BookID per price change β that is the Type 2 move; Type 4 exists precisely so the book keeps one identity and the factβs FK stays stable.
πΉ Type 4 β the correct report (range join +listagg)
Price valid in the sale month, every price listed
select F.TimeID, F.BranchID, F.BookID, B.BookTitle, B.Author, listagg(P.Price, ';') within group (order by P.Price) as Price, F.Number_of_Books_Soldfrom BookSalesFact F, BookDim B, BookPriceDim Pwhere F.BookID = B.BookIDand B.BookID = P.BookIDand to_date(F.TimeID, 'MonYYYY') >= to_date(P.StartDate, 'MonYYYY')and to_date(F.TimeID, 'MonYYYY') <= to_date(P.EndDate, 'MonYYYY')group by F.TimeID, F.BranchID, F.BookID, B.BookTitle, B.Author, F.Number_of_Books_Sold;
Why listagg β the fact is at month grain; when more than one price period overlaps a month the range join returns several prices, and listagg folds them into one cell (Report 4: C1 in Jan2008 β $23.00;$45.95) instead of duplicating the row.
π‘ Common Mistake:Comparing MonYYYY strings directly β 'Dec2007' > 'Mar2008' alphabetically; convert both sides with to_date before the range test.
βοΈ Core Decision Matrix
Type
Where history lives
Rows per member
Recovers the price at sale time?
Cost
0
nowhere β initial value frozen
1
β
reports drift as reality moves
1
nowhere β overwritten
1
β
the past is silently re-priced
2
in the dimension, as extra rows with suffixed IDs
many
β full
one book is split across version IDs; CurrentFlag finds βnowβ
3
in the dimension, as extra columns
1
β οΈ one step back only
anything older than the previous value is lost
4
in a separate dimension
1 +n history rows
β full
one more join (a range join) per historic query
6
both (2 + 3)
many, same BookID
β full + previous value
dimensionβfact becomes mβm β needs a fix (Β§3)
When It Flips: Type 3 is enough only while "the previous value" is the whole business question. The moment a report must span more than one change β C1 went full price β 20% off β half price β full price β Types 2, 4 or 6 are the only correct answers.
β οΈ Common Mistakes
π‘ Treating Type 0 and Type 1 as different for reporting β they differ in which single value survives, not in whether history exists; both answer historic questions wrong.
π‘ Keying a Type 6 fact on BookID alone β the ID repeats per version, so each fact row joins every version and the measure multiplies β apply one of the three Β§3 fixes.
π‘ Calling Type 4βs history table a plain dimension β its key is the composite (BookID,StartDate,EndDate); it is a weak-entity / bridge-shaped table hanging off the parent, not a second axis of the star.
π§ Active Recall
The fact already stores TimeID. Why can it not just look up the price for that month, instead of the warehouse carrying an SCD at all?
Answer
Short answer: because a flat Price column in the dimension holds one value with no time attached β there is nothing for TimeID to be matched against.
Why:A dimension attribute is unqualified by time β BookDIM.Price describes the book, not the book-on-a-date, so the join returns the same figure for every TimeID. The temporal key must be stored β Types 2, 4 and 6 all introduce (StartDate,EndDate) so the sale month can fall inside a range. Type 3 fakes it β PreviousPrice gives an ordering with no dates, so it can rank two values but never place a fact between them.
Type 2 and Type 4 both keep the complete history. On what grounds does the design choose between them?
Answer
Short answer: on whether the member may keep one identifier β Type 2 mints a suffixed ID per version (C1_3, C1_4), Type 4 keeps C1 and versions a separate table.
Why:Type 2 moves identity into the fact β the factβs FK names the version, so the join is a plain equality, but βall sales of C1β must re-assemble C1_1β¦C1_4. Type 4 keeps the starβs grain β BookDIM stays one row per book and the FK never changes; the price is reached by a range join on the sale month, at the cost of an extra join (and listagg when periods overlap a month).
Type 6 needs no separate identifier per version. What does that cost the star, and how is it repaired?
Answer
Short answer: the dimensionβfact relationship becomes mβm; repair with a surrogate key, dates in the fact, or a bridge table.
Why:Repeated BookID β four C1 rows share one ID, so a fact row keyed only on BookID matches all four. Each fix restores a unique match β a surrogate key makes each version unique Β· (StartDate,EndDate) in the fact selects the version by date Β· a bridge resolves the mβm explicitly.