Data Exploration (Warehouse Validation)

Context: FIT3003_MOC · the step that must run before Building Dimension Tables and after Building Fact Tables · defects it finds are repaired by Data Cleaning (Dirty Data) Parent Framework: Data Engineering Runnable scripts: 30_Projects/FIT3003_Labs/Lab03a_USELOG_clean_to_star.sql · Lab03b_ROBCOR_clean_to_star.sql

Quick Revision

  • 🎯 Objective: the SQL can be flawless and the warehouse still wrong ➔ correctness is established by predicting a record count and comparing it, never by re-reading the code.
  • 📦 Core Components: count the operational tables ➔ predict the join cardinality ➔ count the TempFact ➔ explain every discrepancy.
  • ⚠️ Key Constraint: “Don’t trust the operational databases.” A dirty source produces a fact table of the right shape with the wrong numbers — the row count of the fact can match perfectly while every measure is inflated.

📝 How It Works

1. The four opening questions

  • How many records in the operational DB?select count(*) on every source table — this is the baseline every later number is judged against.
  • How many records in the warehouse? ➔ count the TempFact and the fact; manually-populated dimensions need no probe because their rows were inserted by hand.
  • What kind of data is in the source?select a formatted sample (to_char(log_time,'HH24:MI')) with an order by, so repeats sit adjacent and become visible.
  • How do the warehouse tables look? ➔ the same read on the TempFact, to be diffed against the source sample.

2. Predict the cardinality, then compare

  • A 1– join returns exactly the -side count ➔ if is the 1 side and the side, then regardless of how many students exist.
  • Excess rows ⟹ the 1 side is not unique ➔ a duplicated PK on the 1 side multiplies every matching transaction row; the join is the amplifier, the duplicate is the cause.
  • Missing rows ⟹ orphan FKs on the side ➔ transactions referencing a non-existent parent are dropped by the inner join and vanish silently.

3. Which shrink is legitimate

  • TempFact ➔ Fact is expected to shrink ➔ the single group by collapses every row sharing a dimension-key combination into one, so is normal, not a defect.
  • Source ➔ TempFact must NOT change size ➔ staging only joins and projects; any delta here is a data defect ➔ Data Cleaning (Dirty Data).

4. Equal row counts do not mean equal contents

  • fact_uselog and fact_uselog2 both hold 1363 rows ➔ the dimension-key combinations that occur are unchanged by de-duplication; only Total_Usage differs.
  • Consequence ➔ a row-count check on the fact proves nothing; the check must be made upstream, on the TempFact.

🗂️ Schema

  • Sources joined 1– to .
  • Derived keysTimeID and SemID do not exist in the source; they are banded onto the TempFact by alter add + updateBuilding Fact Tables.

⚙️ Core Implementation

🔹 The probe suite

⚖️ Symptom ➔ Diagnosis Matrix

SymptomLikely causeProbeRepair
duplicate PK on the 1 sidegroup by PK having count(*) > 1select distinct in the TempFact, or clean the source copy
orphan FK dropped by the inner join, or duplicates already in the sidewhere FK not in (select PK …)delete orphans, or accept if the side was itself duplicated
counts match, measures wrongduplicates that survive distinct on the projected columnscompare fact vs fact2 contentsre-stage from a cleaned source
a dimension join reduces the answertwo dimension members share a descriptionselect * from majorDIMgroup on the code, not the description

When It Flips: a row-count check is sufficient while the fault is structural (join fan-out). Once the counts agree, only a content diff of the measures can expose the remaining fault — which is why fact_uselog2 had to be compared value-by-value against fact_uselog.

📊 Exam Execution Trace & Applied Exercises

Manual Execution Trace — the USELOG reconciliation

StepProbeResultVerdict
0count(*) dw.uselogthe side — the prediction
1count(*) dw.studentthe 1 side — irrelevant to the join size
2count(*) tempfact_uselog over prediction
3duplicate probe on dw.student ids with rowcause of the inflation
4count(*) tempfact_uselog2 (distinct)✗ still vs dw.uselog
5duplicate probe on dw.uselog rows with count(*) = 2the source itself is dirty
6verdictthe TempFact is right, dw.uselog is wrong
7count(*) fact_uselog vs fact_uselog2 vs shape identical, Total_Usage differs

Applied Exercise — the band totals as a second reconciliation

Problem: after cleaning, the three TimeID updates report , and rows, and the two SemID updates report and . Does this corroborate the cleaned count?

Final Extracted Output: both banding partitions total the TempFact exactly ➔ every row received exactly one band, so no row was double-updated and none was left null. On the dirty TempFact the same sums reach — the arithmetic is self-consistent either way, which is precisely why row counts alone never certify correctness.

⚠️ Common Mistakes

  • 💡 Reading the code to find the bug ➔ there is no bug in the code; the fault is in the data, and only counting exposes it.
  • 💡 Treating the TempFact ➔ Fact shrink as suspicious ➔ that reduction is the group by doing its job; the staging step is the one that must preserve cardinality.
  • 💡 Declaring victory after one duplicate probe ➔ every source table needs its own check, on the composite key where no single-column PK exists.
  • 💡 Joining to a dimension “for readability” without checking itmajorDIM holds several majors sharing one Major_Name; grouping on the name silently merged them, cutting result rows to Building Dimension Tables.

🧠 Active Recall