DSC3103 Β· Module 3 of 9

Analytical Star Schemas & DuckDB

Model facts, dimensions & OLAP queries for fast analytics (Lab 4)

~56 min to study properly

Transactional databases (OLTP) like PostgreSQL or SQLite are optimised for fast row-by-row inserts and updates. Analytical engines (OLAP) like DuckDB are built for scanning millions of rows across a few columns.

Checkpoint Β· the examiner asks

Contrast Fact tables and Dimension tables in a Star Schema. Why is declaring the exact 'grain' of a fact table critical before designing joins and aggregations?

In one minute

OLTP databases handle row-by-row writes; OLAP engines like DuckDB scan few columns over many rows. A star schema has a central fact table of numeric measures and foreign keys at a declared grain, surrounded by descriptive dimension tables used for filtering and grouping. Declare the grain first: mixing grains makes joins fan out and double-count.

Traps examiners look for
  • Storing descriptive strings (like category names) repeatedly in the fact table.
  • Mixing daily and monthly rows in one fact table.
  • Joining a fact to a many-row dimension and silently multiplying SUMs (fan trap).
Read

Study notes

01OLTP vs OLAP

Databases are built for one of two jobs.

OLTP (transactions)OLAP (analytics)
Typical queryinsert/update one order, look up one customeraverage price by region over 3 years
Rows toucheda fewmillions
Columns touchedall of a rowa few
Storagerow-orientedcolumn-oriented
ExamplesPostgreSQL, SQLite, MySQLDuckDB, BigQuery, Snowflake

DuckDB is "SQLite for analytics": it runs inside your Python process with no server, but uses a columnar, vectorised engine that processes values in CPU-cache-sized batches. On a laptop it can scan Parquet files faster than many clusters on data under ~50 GB.

02Facts, dimensions and the grain

A star schema puts one fact table in the middle and dimension tables around it.

  • The fact table stores measurements: numbers you sum, average or count (price_ugx, rainfall_mm, litres), plus foreign keys to the dimensions. It is long and narrow.
  • Dimension tables store descriptions used to filter and group: dim_market (market, district, region), dim_commodity (name, category, unit), dim_date (day, month, quarter, year, is_weekend). They are short and wide.

Before writing SQL you declare the grain: what exactly one fact row represents. Here: one commodity in one market on one day. Every measure in the fact table must be true at that grain. That's what lets you compute fact-table size directly: |fact| ≀ |dates| Γ— |markets| Γ— |commodities|.

03Surrogate keys and why not join on strings

Dimensions get a surrogate key β€” a meaningless integer like market_key = 7 β€” rather than joining facts on market_name = 'Owino'.

Why:

  • Integers are smaller and faster to join and index than strings.
  • Names change ("Owino" β†’ "St. Balikuddembe Market"); with a surrogate key you update one dimension row, not millions of fact rows.
  • You can represent things the source doesn't have, like an Unknown member (key = 0) for facts whose dimension value is missing β€” so no fact row has a NULL key.
  • History can be tracked (SCD Type 2): one market can have several dimension rows with different validity dates.

04The fan trap

A fan trap happens when a join matches one fact row to several dimension rows. The fact row is repeated once per match, so SUM(price_ugx) is multiplied β€” with no error message.

Typical causes:

  • the dimension has duplicate keys (the same market_key twice, e.g. loaded twice);
  • you join to a table at a finer grain (e.g. daily facts joined to a table with one row per market per vendor);
  • you mix grains in the fact table (daily rows next to monthly totals).

Defences: enforce unique keys on dimensions, declare the grain and stick to it, and check row counts before and after joins β€” a join to a dimension must never increase the number of fact rows.

Watch it done

Worked examples

Worked example 1

Sizing a warehouse from its grain

The warehouse covers 12 markets and 8 commodities for 2 full years (730 days) with complete coverage. How many fact rows, and how many dimension rows in total?

    Worked example 2

    Spotting a fan trap

    SUM(price_ugx) for Owino on 2025-03-01 should be 4,000 UGX but the report shows 12,000. dim_market has three rows with market_key = 3. Explain and fix.

      Vocabulary

      Key terms

      In-process OLAP: why DuckDB is the 'SQLite for Analytics'

      DuckDB runs embedded inside your Python process with zero server setup, yet uses a columnar-vectorised execution engine written in C++ that can query Parquet files directly on disk faster than many distributed clusters on datasets under 50 GB.

      Practise

      Quick check

      0 of 8 correct so far. Wrong answers come with an explanation β€” and the buddy can unpack them.

      Q1In our Star Schema, we cover 5 markets, 4 commodities, and 365 days with complete daily observations. How many rows are in fact_market_prices?

      Q2Which table should store the attribute 'commodity_category = Cereals'?

      Q3Which belongs in a fact table?

      Q4What is the grain of fact_market_prices?

      Q5Why use a surrogate integer key instead of the market name?

      Q6Joining facts to a dimension increased the row count from 7,300 to 9,100. What does this indicate?

      Q7DuckDB is best described as…

      Q8Where should 'is_weekend' live?

      Exam-style

      Written practice

      Short answer5 marks

      Define the grain of a fact table and explain what goes wrong if you mix grains.

      Short answer5 marks

      Explain why dimension tables use surrogate keys and how an Unknown member avoids NULL foreign keys.

      Long answer12 marks

      Design a star schema for the market-price data. List the fact table (grain, measures, keys) and three dimensions with attributes, then write a DuckDB query for the average price per region and commodity category in 2025, and say how you'd check it isn't affected by a fan trap.

      Watch

      Videos

      Fresh picks are chosen by AI every day.
      Hands-on

      Star Schema Grain & Fan-Trap Simulator

      4 guided tasks: predict, experiment, explain β€” then challenge yourself. Plus 2 coding exercises you run right in the browser.

      Start the lab