Data Engineering glossary
What is a Snowflake Schema?
A snowflake schema is a variant of the star schema in which dimension tables are normalised into further tables, for example product pointing to category and category pointing to department. It removes repeated text and gives each level its own attributes, at the cost of more joins and a model that is harder to read.
How it works
In a star schema, dim_product carries category and department as plain columns, repeated for every product. In a snowflake schema those levels move into their own tables: the product row holds a category_key, and the category row holds a department_key. Drawn out, the dimensions branch outward like a snowflake.
That brings some benefits:
- Repeated text is stored once.
- Each level has a natural home for its own attributes, such as a category manager or a department budget code.
- A hierarchy that is maintained in a separate source system can be loaded as it is.
The costs are more joins in every query, a model analysts find harder to navigate, and BI tools that need more configuration. In columnar warehouses the storage saved by snowflaking is small, because repeated values in a column compress well. For that reason most teams keep dimensions denormalised (a star) and snowflake only where a level genuinely has many attributes of its own, or where a hierarchy is shared by several dimensions.
Note that “snowflake schema” is a modelling term and has nothing to do with the Snowflake data warehouse product.
Example
The same question, revenue by department, needs three joins on a snowflaked product dimension instead of one on a star.
CREATE TABLE dim_department (department_key int PRIMARY KEY, department text);
CREATE TABLE dim_category (category_key int PRIMARY KEY, category text, department_key int);
CREATE TABLE dim_product (product_key int PRIMARY KEY, product text, category_key int);
CREATE TABLE fact_sales (product_key int, amount numeric(10,2));
INSERT INTO dim_department VALUES (1, 'Footwear'), (2, 'Kitchen');
INSERT INTO dim_category VALUES (10, 'Running shoes', 1), (20, 'Cookware', 2);
INSERT INTO dim_product VALUES (100, 'Trail runner', 10), (200, 'Iron skillet', 20);
INSERT INTO fact_sales VALUES (100, 49.99), (100, 49.99), (200, 35.00);
SELECT d.department, sum(f.amount) AS revenue
FROM fact_sales f
JOIN dim_product p ON p.product_key = f.product_key
JOIN dim_category c ON c.category_key = p.category_key
JOIN dim_department d ON d.department_key = c.department_key
GROUP BY d.department ORDER BY d.department;
department | revenue
------------+---------
Footwear | 99.98
Kitchen | 35.00
Common mistakes
- Snowflaking every dimension out of habit from normalised OLTP design.
- Expecting large storage savings in a columnar warehouse, where compression already removes most repetition.
- Forgetting that a snowflaked level also needs an Unknown member, or products with no category vanish from inner joins.

