What you will be able to do
- Explain how a metric view differs from a standard view and why it gives consistent KPIs
- Define sources, joins, filters, fields and measures in metric view YAML and query measures with MEASURE()
- Choose between aggregated and unaggregated materializations for a metric view
- Predict when relaxed-mode query rewrite serves stale data and when materialization is blocked
1.Why metric views: define once, group by anything
A standard view fixes its aggregations and groupings when you create it. To see revenue by month and by region, you need two views, and each repeats the revenue formula. A Unity Catalog metric view keeps the measure definitions separate from the fields (dimensions) used to group and filter them. You define a metric such as revenue per distinct customer once. Users can then group it by any available field, and the query engine generates the correct computation at runtime.
This is the governance benefit. Every KPI has one registered definition in Unity Catalog, so everyone in the organisation reports the same value for the same metric.
| Component | What it defines | Example |
|---|---|---|
| Source | The base table, view, or SQL query containing the data | samples.tpch.orders |
| Joins | Relationships to other tables, views, and metric views | Join orders with customers on customer_key |
| Filters | Conditions that scope the source data | status = 'completed' |
| Fields (dimensions) | Columns used to group, filter, and aggregate metrics | Product category, Order month |
| Measures | Column aggregations that produce metrics | SUM(o_totalprice) as Total Revenue |
A source can be any table-like Unity Catalog asset, including tables, views, materialized views, streaming tables and other metric views, or it can be a SQL query. A gold star schema therefore plugs in directly. Use the fact table as the source and the dimensions as joins. If the source is a SQL query containing a JOIN, declare primary and foreign key constraints with the RELY option for the best performance.
Checkpoint 1 of 6· Check yourself
Finance and sales each built standard views that compute 'average order value' slightly differently, and their dashboards disagree. What does a metric view change?
A metric view defines the measure once and computes it at query time for whatever grouping the user asks for, so the two teams stop maintaining separate formulas.
“As a result, every user across the organization reports the same value for the same KPI, which eliminates inconsistent reporting”Source: docs.databricks.com
2.Fields, measures and the MEASURE() function
Fields behave like ordinary columns: you can use them in SELECT, WHERE and GROUP BY. A field can be a categorical column such as region, or an unaggregated numeric column such as price. Measures must be aggregate expressions, and they have no fixed level of aggregation; the grouping in each query decides it. A measure can combine aggregations, as in a ratio, or apply a FILTER (WHERE ...) clause.
measures:
# Simple count measure
- name: Order Count
expr: COUNT(1)
# Sum aggregation measure
- name: Total Revenue
expr: SUM(o_totalprice)
# Distinct count measure
- name: Unique Customers
expr: COUNT(DISTINCT o_custkey)
# Calculated measure combining multiple aggregations
- name: Average Order Value
expr: SUM(o_totalprice) / COUNT(DISTINCT o_orderkey)You can define a metric view in Catalog Explorer, with built-in YAML validation, or in SQL by wrapping the YAML in a CREATE VIEW statement that has a WITH METRICS clause and placing the YAML between $$ delimiters.
Checkpoint 2 of 6· Fill the gap
Which keyword turns this CREATE VIEW statement into a metric view definition?
CREATE OR REPLACE VIEW catalog.schema.orders_materialized WITH ? LANGUAGE YAML ASA metric view is created with CREATE VIEW ... WITH METRICS LANGUAGE YAML AS, followed by the YAML between $$ delimiters.
Source: docs.databricks.comNo. Every measure in a query must be wrapped in the MEASURE() aggregate function, so you list fields and measures explicitly instead of using SELECT *. You also can't join a metric view directly to other tables at query time. To combine it with another table, wrap the metric view query in a CTE and join the CTE result.
SELECT
`Order Month`,
`Order Status`,
MEASURE(`Order Count`),
MEASURE(`Total Revenue`)
FROM orders_metric_view
GROUP BY ALL
ORDER BY `Order Month`;Checkpoint 3 of 6· Exam question
A team builds a standalone `daily_revenue_by_region` materialized view on top of an hourly-updating `fact_sales` table. Stakeholders want the view to reflect new source data within a few minutes of it landing, but they do not want a pipeline running on a fixed timer when no new data has arrived, since that would waste serverless compute. Which materialized view refresh configuration best meets this requirement?
Correct answer: A — Create the materialized view with `TRIGGER ON UPDATE` so a refresh is automatically scheduled only when the underlying source tables actually change, avoiding unnecessary compute when `fact_sales` is idle.
- A. The `TRIGGER ON UPDATE` refresh mode watches the source tables and schedules a refresh only when they actually change, which delivers near-real-time freshness without running compute on a fixed cadence while the source is idle. This matches both the freshness and the cost-avoidance requirement.
- B. An ad-hoc materialized view only refreshes when someone explicitly runs the refresh command, so freshness depends entirely on an analyst remembering to trigger it. That does not reliably deliver updates within a few minutes of new data landing.
- C. A per-minute cron schedule does refresh quickly, but it runs on a fixed timer even when `fact_sales` has not changed, which is exactly the wasted-compute pattern the team wants to avoid. Trigger-based refresh reacts to actual changes instead.
- D. A materialized view always physically stores its results and only updates them on refresh; it does not recompute live at query time the way a standard view does. Querying it without ever refreshing would just return stale, previously materialized data.
3.Materializing a metric view: aggregated vs unaggregated
Metric views compute at runtime, but you can still pre-compute them. Add a top-level materialization block to the YAML, and Databricks creates a managed Lakeflow pipeline that builds and maintains the materialized views on the schedule you set. You keep querying the metric view itself. The optimizer uses aggregate-aware query rewriting: it takes the fast path and reads a suitable materialization when one exists, and otherwise falls back to the source data. The metric view remains the single source of truth for what the metric means. Materialization only changes how it is stored. It requires serverless compute for the pipelines and Databricks Runtime 17.3 or above.
materialization:
schedule: every 6 hours
mode: relaxed
materialized_views:
- name: baseline
type: unaggregated
- name: revenue_breakdown
type: aggregated
dimensions:
- category
- color
measures:
- total_revenue
cluster_by:
cols:
- category
- color| Aspect | aggregated | unaggregated |
|---|---|---|
| What it stores | Pre-computed results for specific dimensions and measures | The full data model: source, joins, filter, and fields |
| Which queries it serves | Exact match on its dimensions, or rollup to a subset of them | Any query shape |
| Use it when | Specific dimension and measure combinations are queried frequently | Source transformations or joins are expensive, query patterns are unpredictable, or all users must see consistent data |
| Trade-off | Best performance, but only for the shapes it covers | Wider coverage with less performance lift; no benefit over a direct table reference without a selective filter |
To get good aggregated coverage, materialize at the finest grain your queries need, and include the columns that users filter on. A materialization at (region, sku, event_day) can serve GROUP BY region, GROUP BY region, event_month, and GROUP BY sku with WHERE region = 'US'. Don't go so fine that most groups contain a single row, such as millisecond timestamps, which only adds storage. Non-additive measures like COUNT(DISTINCT), MEDIAN and percentiles can't be rebuilt from partial results, so they need an exact match. If both types exist, Databricks computes the aggregated materializations from the unaggregated one, which gives a consistent snapshot.
Checkpoint 4 of 6· Check yourself
An aggregated materialization stores number_of_suppliers, defined as COUNT(DISTINCT supplier_id), by (category, color). A user queries number_of_suppliers grouped only by category. What happens?
Distinct counts can't be re-aggregated from partial groups, so only an exact-match materialization (like suppliers_by_category in the docs) can serve the query. Otherwise it falls back to source; it doesn't fail.
“Non-additive measures can't be re-aggregated from partial results (for example, COUNT(DISTINCT), MEDIAN, and percentiles) and require an exact match against a materialization.”Source: docs.databricks.com
Sources5
4.Relaxed mode, freshness, and what blocks materialization
The materialization mode is always relaxed. In relaxed mode, query rewrite checks only whether a candidate materialization contains the fields and measures the query needs. It does not check freshness, SQL settings such as TIMEZONE or ANSI_MODE, or determinism. So two queries against the same metric view can see different data: a query that matches a materialization reads its last refresh, while a query that doesn't match reads live source data. To limit the gap, schedule materialization refreshes to run after your source pipeline completes, or use an unaggregated materialization so that every query reads the same snapshot.
A materialization is computed once, using the owner's identity, and then shared with everyone. Serving it to other users would skip the per-user checks that row filters, column masks and ABAC policies apply at query time, so Databricks blocks materialization if the view or any of its source tables uses them.
The same reasoning rules out invoker-dependent expressions, whose results depend on who runs the query, such as current_user() or is_member(). Databricks validates this when you create, alter or refresh a materialization. These operations fail with METRIC_VIEW_MATERIALIZATION_WITH_INVOKER_DEPENDENT_EXPRESSIONS_NOT_SUPPORTED.
Checkpoint 5 of 6· Match them up
Match each situation to how a materialized metric view behaves
Tap a term, then the definition that fits it.
Relaxed mode checks only field and measure coverage, so the matched path can be stale. Per-user security and invoker-dependent expressions are incompatible with a shared, pre-computed result.
“Queries that match a materialization use the last refresh. Queries that don't match fall back to source and return live data.”Source: docs.databricks.com
Checkpoint 6 of 6· Exam question
A materialized view aggregating `fact_orders` by region is expected to refresh incrementally, but `EXPLAIN CREATE MATERIALIZED VIEW` shows every refresh performing a full recompute of the entire view instead of merging only the changed rows. The source `fact_orders` table is a Delta table that has never had any table properties explicitly set. What is the most likely cause, and how should the team fix it?
Correct answer: A — The source table does not have row tracking enabled, which incremental refresh needs to identify which rows changed; run `ALTER TABLE fact_orders SET TBLPROPERTIES (delta.enableRowTracking = true)` to fix it.
- A. Incremental refresh relies on row tracking to identify exactly which rows in the source table were inserted, updated, or deleted since the last refresh, and a Delta table without it forces the engine to fall back to a full recompute. Enabling `delta.enableRowTracking` on the source table is the documented fix for this exact symptom.
- B. Incremental refresh is not limited to single-column aggregations; materialized views commonly aggregate multiple columns and still refresh incrementally when the underlying source supports change tracking. Column count is not the mechanism that determines whether a refresh falls back to full recompute.
- C. Materialized views and their source tables can live in different catalogs within the same metastore, and catalog placement alone is not what determines incremental versus full refresh eligibility. The real gate is whether the source table exposes row-level change information.
- D. Incremental refresh is not something a workspace admin globally enables or disables; eligibility is evaluated per materialized view based on whether its source tables support change tracking. There is no workspace-wide switch that would explain this specific behavior.
Sources5
Exam traps
Each one states something that sounds right. Open it to see what is actually true.
1.One aggregated materialization at a coarse grain can serve any grouping of its measures through rollup.Why is that wrong?
An aggregation serves only queries on exactly its dimensions or on a subset of them, and non-additive measures need an exact match. Databricks recommends several aggregated materializations for different query shapes.
Covered in Materializing a metric view: aggregated vs unaggregated
2.Query rewrite to a materialization guarantees the result is as fresh as the source data.Why is that wrong?
Relaxed mode checks only that the fields and measures are present. It skips the freshness check, so matched queries return the last refresh while unmatched queries return live data.
Covered in Relaxed mode, freshness, and what blocks materialization
3.You can materialize a metric view over tables protected by row filters or column masks, and the security policies will still apply per user.Why is that wrong?
A materialization is computed once under the owner's identity, so it would bypass per-user controls. Materialization is therefore blocked when RLS, column masks or ABAC policies are present.
Covered in Relaxed mode, freshness, and what blocks materialization
Sources
Every claim above is drawn from one of these pages, quoted as it was written on the date shown.
- 1.
“Standard views lock in aggregations and groupings at creation time.”
↩︎ Why metric views: define once, group by anything“users can group by any available field. The query engine generates the correct computation.”
↩︎ Why metric views: define once, group by anything - 2.
“A table-like asset is any Unity Catalog object that exposes a tabular schema and supports SELECT queries”
↩︎ Why metric views: define once, group by anything“set primary and foreign key constraints on underlying tables and use the RELY option for optimal query performance.”
↩︎ Why metric views: define once, group by anything“Measures are expressions that produce results without a pre-determined level of aggregation.”
↩︎ Fields, measures and the MEASURE() function“Fields, also called dimensions, are metric view columns that you can use in SELECT, WHERE, and GROUP BY clauses at query time.”
↩︎ Fields, measures and the MEASURE() function“As a result, every user across the organization reports the same value for the same KPI, which eliminates inconsistent reporting”
↩︎ Checkpoint - 3.
“wrap the YAML definition in a CREATE VIEW statement with the WITH METRICS clause, and place the YAML between $$ delimiters”
↩︎ Fields, measures and the MEASURE() function - 4.
“Because measures require the MEASURE() function to evaluate properly, you must specify individual columns rather than using SELECT *.”
↩︎ Fields, measures and the MEASURE() function“Metric views cannot be directly joined with other tables at query time.”
↩︎ Fields, measures and the MEASURE() function - 5.
“At query time, the query optimizer routes queries to the best materialized view using automatic aggregate-aware query matching (query rewriting).”
↩︎ Materializing a metric view: aggregated vs unaggregated“From that definition, Databricks creates a managed Lakeflow pipeline that builds and maintains the materialized views.”
↩︎ Materializing a metric view: aggregated vs unaggregated“Each materialized view is either aggregated, which pre-computes specific dimensions and measures, or unaggregated, which materializes the full data model.”
↩︎ Materializing a metric view: aggregated vs unaggregated“When both aggregated and unaggregated materializations exist, Databricks computes the aggregated materializations from the unaggregated one.”
↩︎ Materializing a metric view: aggregated vs unaggregated“Alternatively, use an unaggregated materialization to guarantee that all queries read from the same snapshot.”
↩︎ Relaxed mode, freshness, and what blocks materialization“Invoker-dependent expressions, whose result changes based on who runs the query (for example, current_user() or is_member()).”
↩︎ Relaxed mode, freshness, and what blocks materialization“A single aggregation can only serve queries that match its specific dimensions (exact match) or a subset of its dimensions (rollup match).”
↩︎ Exam trap 1“In relaxed mode, automatic query rewrite only verifies whether candidate materialized views have the necessary fields and measures to serve the query.”
↩︎ Exam trap 2“Because a materialization is pre-computed once using the owner's identity, serving it to other users would bypass the per-user access controls”
↩︎ Exam trap 3“Non-additive measures can't be re-aggregated from partial results (for example, COUNT(DISTINCT), MEDIAN, and percentiles) and require an exact match against a materialization.”
↩︎ Checkpoint“Queries that match a materialization use the last refresh. Queries that don't match fall back to source and return live data.”
↩︎ Checkpoint