Skip to content

Expert: design, speed and engines · Schema design and normalisation · topic 44 of 62 · 35 min

Denormalisation, OLTP and OLAP

Explanation

Sometimes you copy data on purpose to make reads fast, for example a cached order total; this is denormalisation, and its price is keeping the copies in sync. App databases that handle many small writes (OLTP) stay normalised, while analytics systems (OLAP) use star schemas: one big fact table surrounded by dimension tables, often inside a cloud data warehouse.

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.
sql
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 | 4498
A cached total. Reading it is one column; keeping it right is now your job.

The 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.

Careful: A stale copy of money is worse than a slow query. If a cached total can drift, show it only on screens where "a few minutes old" is acceptable, and compute payable amounts from the source rows.

OLTP and OLAP: two kinds of workload

OLTP (online transaction processing)OLAP (online analytical processing)
Typical questionPlace this order; show order 105Revenue per region per quarter for three years
Rows touchedA few rows per queryMillions of rows per query
WritesMany small inserts and updates all dayLoaded in batches (hourly or nightly)
DesignNormalised (3NF)Star schema, denormalised on purpose
StorageRow-oriented: a whole row is stored togetherUsually columnar: each column stored together, so a sum reads only that column
ExamplesPostgreSQL, MySQL, SQL Server behind an appBigQuery, 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.

output
             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)
A small star schema. Reports join the fact table to whichever dimensions the question needs.
sql
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 rows
Every star-schema report has this shape: join the fact to dimensions, filter, group, sum.

Dimensions 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

Denormalisation

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.

sql
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;
On the small star schema of this topic: Electronics 8 units 15992, Grocery 25 units 8900, Kitchen 4 units 5800.

Practice (0/3 done)

Task 1 · Code · SQL

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.

Task 2 · Code · SQL

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.

Task 3 · Code · SQL

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

Question 1

1.What is the main cost of a cached orders.total column?

Question 2

2.Which workload is OLAP?

Question 3

3.In a star schema, what is in the fact table?

Question 4

4.Why do warehouses often store data in columns rather than rows?

Question 5

5.A store moves from the West region to North. Old sales must still report under West. Which approach keeps that history?

Read more (free)

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.

From Choosing a database: DynamoDB, caches and warehouses

A weekly sales report over five years of data, run by the business team, is typical of...Related

Answer: OLAP

Big read-only analysis over history is OLAP.

From Theory questions you will be asked

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.

From Core Google Cloud services

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

  1. 2 more topics in “Schema design and normalisation” before its exam: Data types, naming and good habits, Design project: an online store.
  2. Module 9 exam: Schema design and normalisation · 13 questions · 25 min, without help. 5 topics of this module still to finish first.
  3. Then the next module, Indexes, query plans and views. 62 topics left in the course before the final exam.

← Previous: Normalisation: 1NF, 2NF, 3NF and BCNF