What you will be able to do
- Separate a gold-layer model into fact and dimension tables arranged as a star schema
- Choose between materialized views and streaming tables for dimensions and facts
- Pick a key strategy for dimensions that keeps fact-to-dimension joins intact after a full refresh
- Explain how materialized views pre-compute results, how they refresh, and where they fall short
Key concept
Materialized view — A query result that Databricks stores and keeps up to date, often incrementally, so readers query stored results instead of recomputing the query each time. It is the pre-computation building block behind both gold-layer dimensions and metric view materializations.
1.Facts, dimensions and the star schema
Analysts and BI tools query efficiently when the gold layer is organised by what each table is responsible for. Dimensional modeling splits the data two ways. A fact table records events: each row is one order, click or sale, described mostly by keys and numeric measures. A dimension table holds the descriptive context for those events, such as customers, products or dates, with one row per business entity.
The star schema puts one fact table at the centre and joins it out to its dimensions through their keys. Each table has a single, clear job, so the model is easy to query and easy to reason about. In the medallion architecture the star schema lives in the gold layer: bronze and silver handle ingestion and cleaning, and gold materialises the facts and dimensions that downstream consumers read.
| Aspect | Fact table | Dimension table |
|---|---|---|
| What a row represents | One occurrence of an event (an order, a click, a sale) | One business entity (a customer, a product, a date) |
| Typical contents | Mostly keys and numeric measures | Descriptive attributes |
| How it connects | References dimensions by key | Joined to the fact table through its key |
| Example from the docs | fact_orders | dim_customer, dim_date |
Checkpoint 1 of 4· Check yourself
A fact table needs to show each order's customer region. Which approach matches the recommended star-schema design?
Facts stay narrow, holding keys and measures. Descriptive attributes such as region stay in the dimension and are pulled in with a join when the query runs.
“Facts reference their dimensions by key rather than duplicating descriptive attributes.”Source: docs.databricks.com
Sources1
2.Materialized views for dimensions, streaming tables for facts
Match each dataset type to the way the table changes. Dimensions are built as materialized views that recompute efficiently from cleaned silver data whenever inputs change. If you need to keep a dimension's change history, use a streaming table with SCD Type 2 instead (AUTO CDC with STORED AS SCD TYPE 2). Facts are built as streaming tables that read the silver stream incrementally.
CREATE OR REFRESH MATERIALIZED VIEW dim_customer
COMMENT "Customer dimension"
AS SELECT customer_id, customer_name, region, signup_date
FROM customers_silver;Checkpoint 2 of 4· Fill the gap
Complete the fact table definition so it reads the silver orders incrementally, as the docs recommend for facts.
CREATE OR REFRESH ? TABLE fact_orders
COMMENT "One row per order line, keyed to dimensions"
AS SELECT
order_id,
customer_id, -- foreign key to dim_customer
product_id, -- foreign key to dim_product
order_date, -- foreign key to dim_date
quantity,
amount
FROM STREAM(orders_silver);Facts are built as streaming tables, and the STREAM(orders_silver) source reads silver incrementally. A materialized view is the default for dimensions, not facts.
Source: docs.databricks.comKeys. Use a natural key, an identifier that already exists in the source such as an order number, when it is stable, because it clusters and joins well. Use a surrogate key only when a source reuses or changes IDs. Even then, avoid a hash surrogate such as sha2(natural_key). Hash values scatter physically adjacent rows across files, which hurts liquid clustering and Z-order. Instead, derive an order-preserving surrogate deterministically from the natural key. Because the same entity always gets the same surrogate, the key survives a full refresh of the dimension. An IDENTITY column works only if the upstream table is append-only and never full-refreshed, since a rebuild can assign new IDs to existing entities and silently break fact joins.
Date dimension. Don't ingest dim_date from a source. Generate it as a small materialized view using sequence() and explode() over a date range. It is static reference data, cheap to compute, and makes date-based joins simpler throughout the model.
Sources1
3.How materialized views pre-compute and refresh
You query a materialized view just as you would a table. Because its results are already computed, those queries can run much faster than queries against a regular view. A materialized view is a declarative pipeline object made of a defining query, a flow that updates it, and the cached results. When you create one outside a Lakeflow pipeline, Databricks creates a pipeline to refresh it, which appears with type MV/ST under Jobs & Pipelines. Unity Catalog stores the view's metadata. Some backing data lives in the __databricks_internal catalog.
Refresh is often incremental. Databricks tracks upstream changes and processes only what it needs to. In the docs' sales_report example, new customers and transactions in three countries cause just three rows to update, not the whole report. Correctness comes before speed, though: a materialized view always returns the correct result, even when that means recomputing from scratch. That gives you the following limitations to design around:
- Some input changes force a full recomputation, which can be expensive.
- Refresh latency is seconds to minutes, not milliseconds.
- Not every computation can be done incrementally.
- If a UDF's behaviour changes in a way Databricks can't detect, such as an upgraded library, you must run a full refresh yourself.
- Materialized views can't be the source or target of CLONE.
With these limits in mind, the docs recommend that BI tools query gold materialized views directly. Because the pipeline refreshes them incrementally, you get near-real-time results without a separate reporting ETL step.
Checkpoint 3 of 4· Check yourself
A team wants a dashboard tile to reflect each new transaction within milliseconds and plans to back it with a materialized view. What is the problem?
Materialized views are not built for low latency, because refreshes take seconds to minutes. BI tools can query them, refresh is often incremental, and standalone materialized views also exist.
“The latency of updating a materialized view is in the seconds or minutes, not milliseconds.”Source: docs.databricks.com
Checkpoint 4 of 4· Exam question
A dimensional model for a retail analytics workload includes a `dim_product` table. The `unit_price` of a product changes periodically due to promotions. Analysts need every historical `fact_sales` row to report against the price that was in effect at the time of the sale, while still being able to look up the current price for planning. Which design should the team use for `dim_product`?
Correct answer: A — Model `dim_product` as a Slowly Changing Dimension Type 2: insert a new row with a surrogate key and effective-date range on each `unit_price` change, and have `fact_sales` reference the key active at sale time.
- A. A Type 2 slowly changing dimension preserves history by inserting a new surrogate key and effective-date range each time an attribute like `unit_price` changes, letting historical fact rows keep pointing at the key that was valid on the sale date. Joining to the row with no end date still gives the current price for planning queries.
- B. A Type 1 update overwrites the attribute in place, which loses the price that was actually in effect when past sales occurred. Every historical fact row would silently start reporting against the current price instead of the price at the time of sale.
- C. Pushing a slowly changing attribute into the fact table as a degenerate dimension abandons the dimensional structure and forces every downstream query to read raw fact rows for pricing context instead of a governed dimension. It also does not solve the underlying versioning problem, it just relocates it.
- D. A current-state dimension paired with a manually joined history table reintroduces the same versioning problem one layer down, since analysts must hand-write the point-in-time join logic on every query instead of the dimension carrying it natively. This is fragile and easy to get wrong compared to a native Type 2 design.
Exam traps
Each one states something that sounds right. Open it to see what is actually true.
1.Because facts get aggregated, fact tables should be materialized views and dimensions should be streaming tables.Why is that wrong?
It's the other way around. Facts are streaming tables fed incrementally from silver. Dimensions are materialized views, or SCD Type 2 streaming tables when you need history.
Covered in Materialized views for dimensions, streaming tables for facts
2.A sha2() hash of the natural key makes a good surrogate key because it is deterministic.Why is that wrong?
Hash values are deliberately random, so physically adjacent rows end up scattered across files, which defeats liquid clustering and Z-order. Use an order-preserving surrogate derived deterministically from the natural key.
Covered in Materialized views for dimensions, streaming tables for facts
Sources
Every claim above is drawn from one of these pages, quoted as it was written on the date shown.
- 1.
“Fact tables hold the events or measurements you care about, such as orders, clicks, or sales.”
↩︎ Facts, dimensions and the star schema“place one fact table in the middle and join it out to several dimension tables through their keys”
↩︎ Facts, dimensions and the star schema“A deterministic key survives a full refresh or rebuild of the dimension, which keeps existing fact-to-dimension joins intact.”
↩︎ Materialized views for dimensions, streaming tables for facts“Build a dim_date as a simple materialized view generated with sequence() and explode() over a date range, rather than ingesting it from a source.”
↩︎ Materialized views for dimensions, streaming tables for facts“Because IDENTITY values are assigned as rows are inserted, a rebuild can reassign different IDs to the same entity”
↩︎ Materialized views for dimensions, streaming tables for facts“Use downstream BI tools to query gold materialized views directly.”
↩︎ How materialized views pre-compute and refresh“Build dimension tables as materialized views (or as streaming tables with slowly changing dimension (SCD) Type 2 when you need history).”
↩︎ Exam trap 1“A hash is deliberately random, which is bad for liquid clustering and Z-order performance because physically adjacent rows end up scattered across files.”
↩︎ Exam trap 2“Facts reference their dimensions by key rather than duplicating descriptive attributes.”
↩︎ Checkpoint“Build fact tables as streaming tables fed incrementally from silver, so gold-layer aggregates stay close to real time.”
↩︎ Prediction - 2.
“Because a materialized view is precomputed, queries against it can run much faster than against regular views.”
↩︎ How materialized views pre-compute and refresh“A materialized view always shows the correct result, even if it requires fully recomputing the query result from scratch”
↩︎ How materialized views pre-compute and refresh“In this example, three rows update instead of the entire sales report.”
↩︎ How materialized views pre-compute and refresh“Unlike standard views, which recompute results on every query, materialized views cache the results and refresh them on a specified interval.”
↩︎ Key concept“The latency of updating a materialized view is in the seconds or minutes, not milliseconds.”
↩︎ Checkpoint