Pivoted Fact Tables

Context: FIT3003_MOC · route A for enforcing a determinant dimension — and the ETL job it creates Parent Framework: Determinant Dimensions

Quick Revision

  • 🎯 Objective: shift a dimension’s key identifier into the fact measures ➔ each fact record becomes a matrix between the surviving dimension keys and one measure per member of the removed dimension.
  • 📦 Core Components: build the determinant version first ➔ the pivoted version is assembled from its fact tables, not from the source again.
  • ⚡ Key Constraint: the determinant fact was built by inner join, so absent combinations have no row; the pivoted fact must show them as ➔ Cartesian product outer join nvl.

📝 How It Works

1. What the pivot does to the schema

  • Determinant version ➔ with a dashed TestComponentDIM holding Listening, Reading, Writing, Speaking, Overall.
  • Pivoted version ➔ TestComponentDIM is deleted and the fact carries ; the remaining four dimension tables are re-used unchanged.
  • Row-count arithmetic ➔ the pivoted fact must be the full grid: rows.
  • Naming ➔ Chapter 10 calls this the column-based solution in the fact; the alternative, keeping the attribute as a key column with one row per member, is the row-based solution ➔ One-Attribute Dimensions.

2. Pivoting without a determinant dimension — Private Taxi

  • Business rules state there is no determinant dimension ➔ a dimension may still be shifted purely because its membership is small and fixed.
  • Five cars ➔ keeps DriverNo, MonthYear and gains CarNo1 … CarNo5 plus measures (Total_Kilometers_Car1, Total_Fuel_Used_Car1, Total_Income_Car1, …).
  • Query cost explodes ➔ retrieving the five cars’ make and year needs five aliases of CarDim and five join conditions in one where clause.

3. The lightweight pivot idiom — build the grid from a dimension (Ch11 Review Fact)

  • When it applies ➔ only one dimension survives the pivot, so the row grid is that dimension itself; no Cartesian product is needed.
  • Two statements ➔ create table … as select CategoryID, 0 as Num_of_1Star_Reviews, … from CategoryDim; then one update filling each column from a correlated subquery over the determinant fact.
  • nvl still mandatory ➔ a category with no -star review returns NULL from the subquery and would overwrite the initialised ➔ nvl((select …), 0).
  • No trim step ➔ the grid is one row per surviving dimension member, so there are no all-zero surplus rows to delete ➔ contrast Step 3 below.
  • The third shape ➔ pulling the pivoted measures back out as a keyed is the Type-Dimension solution, the row-wise counterpart of this column-wise one ➔ Combining Star Schemas.

⚙️ Core Implementation

🔹 Step 0 — the five dimensions (what the source can and cannot give)

🔹 Step 1 — the determinant version (PTE Academic Test)

🔹 Step 2 — the pivoted version, built from Step 1’s facts

🔹 Step 3 — drop the combinations that never happened

⚖️ Core Decision Matrix

AspectDeterminant version (dimension kept)Pivoted version (dimension shifted)
Storage✅ narrower fact, one measure❌ one measure per member; zeroes stored explicitly
Modelling clarity✅ the analysis axis is visible in the star❌ the axis is hidden inside column names
Query joins❌ one extra join to reach the member✅ fewer joins — the member is a column
Correctness enforcement❌ a query may omit it and return nonsense✅ omission is impossible
Dimension’s other attributes✅ retained (Description, MinScore, MaxScore)❌ lost — only the key identifier survives
Adding a new memberone insert into the dimensionalter table … add rebuild the fact
Build effort✅ 4 steps: dims → TempFact → 5 component facts → union❌ 7 steps: those 4, then AllDimensions → 5 outer joins → join back → delete the all-zero rows

When It Flips: the pivot stops being an option once the determinant dimension has many records (a thousand measures is impractical) or many attributes worth keeping — at that point the only remaining enforcement is the user-interface route ➔ Determinant Dimensions.

📊 Exam Execution Trace & Applied Exercises

Manual Execution Trace — the same five reports, run on both facts

ReportDeterminant FinalFact (45 rows)Pivoted FinalFact2 (11 rows)Answer
a. Competent in the overall score3 tables; g.description='Competent' and t.testcomponent='Overall'2 tables; g.description='Competent', read sum(total_students_overall)
b. Took the test in 2017y.year='2017' and a component predicate — without it the number is wrongy.year='2017'; all five sums return the same figure
c. Korean citizensreturns 5 rows, one per componentreturns 1 row, five columns
d. Tested in Australia5 rows of ; pin one component to collapse to one row1 row, in every column
e. Chinese, Proficient, Listening, 20175 tables t.testcomponent='Listening'4 tables; read total_students_listening
f. Japanese, Competent, 2017one more filter — not asked in Task A4 tables; every component read at once ➔ overall but in Listeningper component
  • Reading the trace ➔ every determinant-fact query pays one extra join (TestComponentDim) and one extra predicate; the pivoted fact drops both and hands back all five components as columns — which is why row f is a one-query answer only on the pivot.
  • Any single component gives a headcount ➔ a candidate sits all five components, so whenever no grade is filtered; the component choice only starts to matter once g.description enters the where.

Applied Exercise — the inflation

Problem: FinalFact is asked for the 2017 headcount with where year = '2017' and no component predicate. The operational Test_Result holds candidates who sat in 2017. What comes back?

Final Extracted Output: — a over-count that throws no error, returns one tidy row, and reads as a plausible enrolment figure. Pinning any one component recovers ➔ Determinant Dimensions.

🧠 Active Recall