Data / ML · Schema

Star Schema vs Snowflake Schema

Star schema vs snowflake schema in dimensional modeling: how denormalized dimensions trade storage and maintenance for query simplicity, and when normalizing them into a snowflake pays off.

John Kihiu12 min read

Star schema and snowflake schema are the two standard ways to model dimensions in a data warehouse, and the choice between them is really a choice about where you want the complexity to live: in the storage and maintenance of the dimension tables, or in the joins your queries have to run. Neither is "correct" — they're opposite ends of a denormalization trade-off, and most warehouses end up with a mix.

What a star schema actually looks like

A star schema has one fact table (the measurements — order amount, quantity, revenue) surrounded by denormalized dimension tables (customer, product, date). Denormalized means each dimension is flattened into a single wide table, even if some of its attributes are technically hierarchical or repeat across rows.

SQL · STAR SCHEMA (DENORMALIZED)
CREATE TABLE dim_product (
    product_key      INT PRIMARY KEY,
    product_name     VARCHAR(200),
    category_name    VARCHAR(100),   -- flattened in, not a separate table
    subcategory_name VARCHAR(100),   -- flattened in, not a separate table
    brand_name       VARCHAR(100),   -- flattened in, not a separate table
    supplier_name    VARCHAR(200)    -- flattened in, not a separate table
);

CREATE TABLE fact_sales (
    sale_id      BIGINT PRIMARY KEY,
    product_key  INT REFERENCES dim_product(product_key),
    customer_key INT REFERENCES dim_customer(customer_key),
    date_key     INT REFERENCES dim_date(date_key),
    quantity     INT,
    amount       NUMERIC(12,2)
);

-- One join per dimension, no matter how many attributes you pull
SELECT p.category_name, SUM(f.amount) AS total
FROM fact_sales f
JOIN dim_product p ON f.product_key = p.product_key
GROUP BY p.category_name;

Category, subcategory, brand, and supplier all live as plain columns on dim_product, even though in a normalized world they'd be their own tables. That redundancy is the whole point: any query touching product attributes needs exactly one join.

What a snowflake schema normalizes out

A snowflake schema takes those same dimensions and normalizes them into a hierarchy of smaller tables — category and subcategory become their own tables, referenced by foreign key instead of duplicated as text on every product row.

SQL · SNOWFLAKED DIMENSION HIERARCHY (NORMALIZED)
CREATE TABLE dim_category (
    category_key INT PRIMARY KEY,
    category_name VARCHAR(100)
);

CREATE TABLE dim_subcategory (
    subcategory_key INT PRIMARY KEY,
    subcategory_name VARCHAR(100),
    category_key INT REFERENCES dim_category(category_key)
);

CREATE TABLE dim_product (
    product_key      INT PRIMARY KEY,
    product_name     VARCHAR(200),
    subcategory_key  INT REFERENCES dim_subcategory(subcategory_key),
    supplier_key     INT REFERENCES dim_supplier(supplier_key)
);

-- Same rollup now needs three joins instead of one
SELECT c.category_name, SUM(f.amount) AS total
FROM fact_sales f
JOIN dim_product p ON f.product_key = p.product_key
JOIN dim_subcategory sc ON p.subcategory_key = sc.subcategory_key
JOIN dim_category c ON sc.category_key = c.category_key
GROUP BY c.category_name;

The actual trade-off: storage vs query cost

Normalizing a dimension removes redundant text (a category name repeated across ten thousand product rows becomes one row in a category table), which shrinks storage and makes updates to category names a single-row change instead of a bulk update across every product. That's the entire case for snowflaking.

The cost is that every query touching those attributes needs more joins, and columnar warehouses (BigQuery, Snowflake the product, Redshift) are specifically optimized for the star pattern — scanning a single wide dimension table is cheap, and join elimination or broadcast joins on small denormalized dimensions are fast. Chaining three or four joins to rebuild a hierarchy that could have been three flat columns adds query planning overhead and makes BI tools (which usually expect a flat star for their semantic layers) harder to configure.

Storage is rarely the bottleneck it looks like on paper

A denormalized product dimension with a repeated category name wastes bytes, but text compresses extremely well in columnar storage — a column of ten thousand rows with five distinct category names costs almost nothing after compression. Don't snowflake to save storage in a warehouse where storage is already cheap; snowflake because the hierarchy itself needs independent updates or referential integrity.

When to actually choose each

Default to a star schema for anything BI tools or analysts query directly — dashboards, self-serve reporting, anything where join simplicity and query speed matter more than storage efficiency. Reach for snowflaking selectively, not wholesale, when a specific dimension has a genuine hierarchy that changes independently of the fact table — a product catalog with categories that get renamed or restructured by a merchandising team, where you want that update to happen in one place, not be reflected across every fact-adjacent product row via a slow bulk update or SCD (slowly changing dimension) process.

Most production warehouses are a "galaxy," not purely one or the other

In practice you'll snowflake the one or two dimensions that have real hierarchical churn (product categories, org structures) and keep everything else — date, a flat customer dimension — fully denormalized. Treating the whole schema as an all-or-nothing choice is the mistake; treat it dimension by dimension.

Wrapping up

Star vs snowflake isn't a stylistic preference — it's a direct trade between how much you denormalize (fewer joins, more redundant storage, simpler queries) and how much you normalize (more joins, less redundancy, easier maintenance of hierarchies that actually change). Start with a star schema by default because most analytical queries favor fewer joins, and snowflake only the specific dimensions where the normalized structure earns its keep.

John Kihiu
Acumatica ERP Developer · Laravel Engineer

Independent software engineer in Nairobi specialising in Acumatica customisations, Laravel backends, and tax fiscalisation integrations across East and Southern Africa.