Expert: design, speed and engines · Schema design and normalisation · topic 44 of 62 · 35 min
Denormalisation, OLTP and OLAP
Explanation
When to break the rules
A normalised design is the right default: every fact lives once, so it cannot disagree with itself. But some screens read the same expensive answer thousands of times a minute. If the order list must show each order's total, the database has to add up order_items for every order on every visit. Denormalisation means storing a copy or a pre-computed value on purpose so that common reads are cheap.
- Cached totals: orders.total, or a product's review_count and avg_rating.
- Copied labels: the customer's name on an invoice row, so a printed invoice never changes later.
- Summary tables: daily sales per category, rebuilt every night for a dashboard.
ALTER TABLE orders ADD COLUMN total INTEGER NOT NULL DEFAULT 0;
UPDATE orders
SET total = (SELECT SUM(qty * price) FROM order_items WHERE order_items.order_id = orders.id);
SELECT id, total FROM orders WHERE id IN (101, 106);
-- 101 | 1780
-- 106 | 4498The cost: keeping copies in sync
Every copy can go stale. If someone adds a line to order 101 and forgets to update orders.total, the screen and the bill disagree. You must choose how to keep copies right: update both in the same transaction (module 8), use a trigger (module 11), or rebuild the summary on a schedule and accept that it is a few minutes old. Denormalise only when a measured slow query needs it, and write down how each copy is maintained.
OLTP and OLAP: two kinds of workload
| OLTP (online transaction processing) | OLAP (online analytical processing) | |
|---|---|---|
| Typical question | Place this order; show order 105 | Revenue per region per quarter for three years |
| Rows touched | A few rows per query | Millions of rows per query |
| Writes | Many small inserts and updates all day | Loaded in batches (hourly or nightly) |
| Design | Normalised (3NF) | Star schema, denormalised on purpose |
| Storage | Row-oriented: a whole row is stored together | Usually columnar: each column stored together, so a sum reads only that column |
| Examples | PostgreSQL, MySQL, SQL Server behind an app | BigQuery, Snowflake, Redshift, Microsoft Fabric |
Companies usually keep both. The app writes to the OLTP database; an ETL (extract, transform, load) or ELT job copies the data into a warehouse, where analysts run heavy reports without slowing the app down.
The star schema
A star schema has one fact table in the middle and several dimension tables around it, like the points of a star. A fact is a measurable event: one sale, one train ticket, one UPI payment. The fact table holds numbers (qty, amount) and keys. A dimension describes the "who, what, where, when" of the fact: a date dimension with month and quarter, a store dimension with city and region, a product dimension with category.
dim_date (date_key, full_date, month, quarter)
|
dim_store ---- fact_sales (date_key, store_key, product_key, qty, amount) ---- dim_product
(store_key, city, region) (product_key, name, category)SELECT d.quarter, s.region, SUM(f.amount) AS revenue
FROM fact_sales f
JOIN dim_date d ON d.date_key = f.date_key
JOIN dim_store s ON s.store_key = f.store_key
GROUP BY d.quarter, s.region
ORDER BY d.quarter, s.region;
-- Q1 | North | 3200
-- Q1 | South | 2900
-- Q1 | West | 12497 ... 6 rowsDimensions are deliberately denormalised: dim_store keeps city and region in one table, even though region depends on city. Analysts get simple, fast joins, and since the warehouse is loaded in batches, the update anomalies of topic 2 matter much less. A snowflake schema is the variant where dimensions are normalised into further tables.
Cloud data warehouses, one line each
- Google BigQuery: serverless warehouse on Google Cloud; you load data and run SQL without managing servers.
- Snowflake: a cloud data platform that separates storage from compute and runs on AWS, Azure and Google Cloud.
- Amazon Redshift: the AWS columnar warehouse, with provisioned clusters or a serverless option.
- Azure Synapse Analytics and Microsoft Fabric: Microsoft's analytics services; Fabric is the newer all-in-one platform with its own warehouse.
Slowly changing dimensions (SCD) handle a dimension value that changes, such as a store moving region: Type 1 overwrites the old value, while Type 2 adds a new row with valid_from and valid_to dates so old sales still report under the old region.
Common mistakes
- Denormalising before measuring. Fix: find the slow query first; often an index (module 10) is enough.
- Adding a cached column without a plan to maintain it. Fix: update it in the same transaction or with a trigger, and add a nightly check that compares it with the source.
- Running heavy analytics on the production OLTP database at peak hours. Fix: a read replica or a warehouse.
- A fact table without a clear grain. Fix: write down what one row means ("one product line of one bill") before designing it.
Need help?
Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.
Try:
Watch the lesson
Denormalisation, OLTP and OLAP · 1/7
Normalised design is the default. But sometimes we copy data on purpose to make reading fast. That is denormalisation.
Prefer a video by a teacher? Find free videos on “oltp vs olap star schema fact dimension tables data warehouse explained” on YouTube ↗
In real life: Sales dashboards at a retail chain
A supermarket chain's billing counters write every bill into an OLTP database at each store. Every night, a pipeline copies the bills into a warehouse as fact_sales, one row per product line, with dimensions for date, store, product and customer segment. In the morning, managers open a dashboard showing revenue by region and category for yesterday, last week and last year. The heavy queries run on the warehouse, so the billing counters never slow down.
SELECT p.category, SUM(f.qty) AS units, SUM(f.amount) AS revenue
FROM fact_sales f
JOIN dim_product p ON p.product_key = f.product_key
JOIN dim_store s ON s.store_key = f.store_key
WHERE s.region = 'West'
GROUP BY p.category
ORDER BY revenue DESC;Practice (0/3 done)
Build a fact-table style summary from SHOP: revenue per month per category, leaving out Cancelled orders. Return month (as 'YYYY-MM' using strftime), category, units (sum of qty) and revenue (sum of qty * price), sorted by month and then category.
The database holds a small star schema: fact_sales (date_key, store_key, product_key, qty, amount) with dim_date (date_key, full_date, month, quarter), dim_store (store_key, city, region) and dim_product (product_key, name, category). Return revenue per quarter and region: columns quarter, region and revenue (sum of amount), sorted by quarter and then region.
Denormalise on purpose: 1) add a column total (INTEGER NOT NULL DEFAULT 0) to orders; 2) fill it for every order with the sum of qty * price of its order_items, using UPDATE with a subquery; 3) return id, status and total of every order.
Quick quiz
Read more (free)
Related help
Before this, learn
This topic builds on these. If something here feels new, open them first.
Related topics
- Data types, naming and good habitsSchema design and normalisation · same module
- Modelling data: entities and relationshipsSchema design and normalisation · same module
- Choosing a database: DynamoDB, caches and warehousesCloud Computing (AWS, Azure, Google Cloud) · Networking and databases: VPC and RDS
- Theory questions you will be askedInterview questions and practice projects
- Core Google Cloud servicesCloud Computing (AWS, Azure, Google Cloud) · Google Cloud and comparing the clouds
- Creating the database: schema, migrations and seed dataFull-Stack Development · Building the backend
Questions people ask about this
Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.
What is the main cost of a cached orders.total column?
Answer: It must be kept in sync with order_items, or it goes stale
Every copy is a promise to update it whenever the source changes.
Which workload is OLAP?
Answer: Total revenue per region per quarter for three years
OLAP scans and sums huge numbers of rows for analysis.
In a star schema, what is in the fact table?
Answer: Measurable events with numbers and keys to the dimensions
Facts are events like sales; dimensions describe them.
Why do warehouses often store data in columns rather than rows?
Answer: A query summing one column reads only that column from disk
Analytics touches a few columns of many rows; columnar storage reads just those columns.
A store moves from the West region to North. Old sales must still report under West. Which approach keeps that history?
Answer: SCD Type 2: add a new dimension row with valid_from and valid_to
Type 2 keeps old rows for old facts and a new row for new facts.
The business team wants sales totals by state across five years of data. Which service is designed for this?Related
Answer: Amazon Redshift
Redshift is a columnar data warehouse for large analytical SQL queries.
A weekly sales report over five years of data, run by the business team, is typical of...Related
How is a VPC network scoped in Google Cloud?Related
Answer: Global, with regional subnets
Google Cloud VPC networks are global resources; each subnet lives in one region.
You add archived INTEGER NOT NULL to a notes table that already has rows. What else does the column need?Related
Answer: A DEFAULT value, such as DEFAULT 0
Existing rows need some value for a NOT NULL column. DEFAULT 0 gives them one.
From Creating the database: schema, migrations and seed data
Pass the quiz (60% or more) or mark this topic as done when you are ready.
Your progress is saved on this device only.
Next steps
- 2 more topics in “Schema design and normalisation” before its exam: Data types, naming and good habits, Design project: an online store.
- Module 9 exam: Schema design and normalisation · 13 questions · 25 min, without help. 5 topics of this module still to finish first.
- Then the next module, Indexes, query plans and views. 62 topics left in the course before the final exam.