---
title: "Denormalization"
description: "Denormalization is the deliberate process of combining or duplicating normalized data — for example, flattening related tables together — to reduce joins and speed up read-heavy analytical queries."
canonical: "https://motherduck.com/glossary/denormalization/"
related:
  - title: "DuckLake Architecture Deep Dive"
    url: "https://motherduck.com/blog/ducklake-architecture-deep-dive/"
  - title: "Data Transformation | MotherDuck Docs"
    url: "https://motherduck.com/docs/integrations/transformation/"
  - title: "Data Recovery | MotherDuck Docs"
    url: "https://motherduck.com/docs/concepts/data-recovery/"
gated_asset:
  title: "DuckLake on MotherDuck"
  url: "https://motherduck.com/product/ducklake/"
---

# Denormalization

> Denormalization is the deliberate process of combining or duplicating normalized data — for example, flattening related tables together — to reduce joins and speed up read-heavy analytical queries.

## Overview

Denormalization reverses some of what normalization enforces: instead of splitting data into many single-purpose tables joined by keys, you merge related data back together, accepting redundancy in exchange for fewer joins and simpler, faster reads. It's the standard trade-off analytical warehouses make, because BI dashboards and reports are read-heavy and tolerant of some data duplication, unlike transactional systems that need to avoid update anomalies.

A typical example: a normalized OLTP schema might store `customers`, `addresses`, and `orders` as three separate tables. A denormalized reporting table joins them once, at load time, into a single wide `orders_report` table that repeats the customer's name and address on every order row. Every downstream query then runs against one table instead of three.

### Example

```sql
-- Denormalize at load time instead of joining at query time
CREATE OR REPLACE TABLE orders_denormalized AS
SELECT
    o.order_id,
    o.order_date,
    o.amount,
    c.customer_name,
    c.region,
    a.city,
    a.country
FROM orders o
JOIN customers c ON c.customer_id = o.customer_id
JOIN addresses a ON a.customer_id = c.customer_id;

SELECT region, SUM(amount) AS revenue
FROM orders_denormalized
GROUP BY ALL;
```

### Trade-offs

Denormalization trades storage and write complexity for read speed and simplicity: updating a customer's name now means updating it everywhere it's duplicated, and the denormalized table takes more disk space. For a data warehouse where writes happen in controlled batch loads and reads happen constantly and ad hoc, that trade is almost always worth it. Star schemas are essentially a structured, half-way form of denormalization — dimensions are flattened, but fact and dimension tables stay separate.

### DuckDB angle

DuckDB's columnar compression means a denormalized wide table costs less storage than it would in a row-oriented database, since repeated string values (like a region name) compress extremely well column by column. Materializing a denormalized table with `CREATE OR REPLACE TABLE ... AS SELECT` is a common pattern for pre-computing a fast, join-free table for dashboards.
