Advanced techniques for metric views
Advanced techniques for metric views enable sophisticated calculations such as moving averages, period-over-period changes, running totals, and complex derived KPIs while maintaining consistency and reusability across your semantic layer. This page explains how to use window measures for time-series analysis and composability for building complex metrics from simpler measures.
This page assumes familiarity with basic metric view modeling concepts. See Model metric views.
The examples on this page use the TPC-H sample dataset, which models a wholesale supply chain. For more information about the TPC-H dataset, see tpch. For an end-to-end tutorial using this dataset with metric views, see Tutorial: Build a complete metric view with joins.
Window measures
This feature is Experimental.
Window measures enable you to define measures with windowed, cumulative, or semi-additive aggregations in your metric views, supporting calculations such as moving averages, period-over-period changes, and running totals. This section includes practical examples.
Define a window measure
Window measures enable defining measures with windowed, cumulative, or semi-additive aggregations. A window measure includes the following required values:
-
order: The dimension that determines the ordering of the window.
-
range: Defines the extent of the window, such as trailing, cumulative, or all data. Possible range values include the following:
current: Includes rows where the window ordering value equals the current row's value.cumulative: Includes all rows where the window ordering value is less than or equal to the current row's value.trailing <value> <unit>: Includes rows from the current row going backward by the specified number of time units, such astrailing 3 months. This does not include the current unit. For example,trailing 3 monthsexcludes the current month.leading <value> <unit>: Includes rows from the current row going forward by the specified number of time units, such asleading 7 days. This does not include the current unit.all: Includes all rows regardless of the window value.
-
semiadditive: Specifies how to aggregate the measure when the order field is not included in the query's
GROUP BY. Possible values includefirstandlast.
Trailing, moving, or leading window measure example
The following example calculates a rolling 7-day count of unique customers who placed orders. This metric helps track customer engagement trends over time by showing how many distinct customers made purchases in the week leading up to each date.
version: 1.1
source: samples.tpch.orders
filter: o_orderdate > DATE'1998-01-01'
dimensions:
- name: date
expr: o_orderdate
measures:
- name: t7d_customers
expr: COUNT(DISTINCT o_custkey)
window:
- order: date
range: trailing 7 day
semiadditive: last
For this example, the following configuration applies:
- order:
datespecifies that the date dimension orders the window. - range: The
trailing 7 dayspecification defines the window as the 7 days before each date, excluding the date itself. - semiadditive:
lastindicates the last value in the 7-day window is used when date isn't a grouping column.
Period-over-period window measure example
The following example calculates day-over-day sales growth by comparing today's revenue (sum of all order prices) to yesterday's revenue. This metric helps identify daily sales trends and shows the percentage change in revenue, which is useful for monitoring business performance and spotting anomalies.
version: 1.1
source: samples.tpch.orders
filter: o_orderdate > DATE'1998-01-01'
dimensions:
- name: date
expr: o_orderdate
measures:
- name: previous_day_sales
expr: SUM(o_totalprice)
window:
- order: date
range: trailing 1 day
semiadditive: last
- name: current_day_sales
expr: SUM(o_totalprice)
window:
- order: date
range: current
semiadditive: last
- name: day_over_day_growth
expr: (MEASURE(current_day_sales) - MEASURE(previous_day_sales)) / MEASURE(previous_day_sales) * 100
For this example, the following conditions apply:
-
Two window measures are used: one for calculating total sales on the previous day and one for the current day.
-
A third measure calculates the percentage change (growth) between the current and previous days.
Cumulative (running) total measure example
The following example calculates cumulative sales revenue from the beginning of the dataset up to each date. This running total shows how much total revenue has been generated over time, useful for tracking progress toward annual revenue goals or analyzing long-term growth patterns.
version: 1.1
source: samples.tpch.orders
filter: o_orderdate > DATE'1998-01-01'
dimensions:
- name: date
expr: o_orderdate
- name: customer
expr: o_custkey
measures:
- name: running_total_sales
expr: SUM(o_totalprice)
window:
- order: date
range: cumulative
semiadditive: last
The following details highlight key parts of this definition:
-
order: Specifies the dimension that defines the ordering of records within the window. In this example,
dateorders the window chronologically. -
range:
cumulativedefines the window as all data from the beginning of the dataset up to and including each date. -
semiadditive:
lastcontrols how the measure is aggregated whendateis not included in the query. When the date dimension is not included in your query'sGROUP BY, the measure returns the last (most recent) cumulative value rather than summing across all dates.
Period to date measure example
The following example calculates year-to-date (YTD) sales revenue. This measure shows the cumulative revenue generated from January 1st of each year up to the current date, resetting at the beginning of each new year. This metric is essential for comparing current year performance to prior years and tracking progress toward annual targets.
version: 1.1
source: samples.tpch.orders
filter: o_orderdate > DATE'1997-01-01'
dimensions:
- name: date
expr: o_orderdate
- name: year
expr: DATE_TRUNC('year', o_orderdate)
measures:
- name: ytd_sales
expr: SUM(o_totalprice)
window:
- order: date
range: cumulative
semiadditive: last
- order: year
range: current
semiadditive: last
The following details highlight key parts of this definition:
-
Two window measures are used: one for the cumulative sum over the
datedimension and another to limit the sum to thecurrentyear. -
The
yeardimension restricts the cumulative sum to calculate it only within the current year.
Semiadditive measure example
The following example calculates account balances, which should not be summed across dates (you can't add Monday's balance to Tuesday's balance to get total balance). Instead, when aggregating across multiple days, the measure returns the most recent balance. However, the measure can still be summed across customers to show total balance across all accounts on a given day.
version: 1.1
dimensions:
- name: date
expr: date
- name: customer
expr: customer_id
measures:
- name: semiadditive_balance
expr: SUM(balance)
window:
- order: date
range: current
semiadditive: last
The following details highlight key parts of this definition:
-
order: Specifies the dimension that defines the ordering of records within the window. In this example,
dateorders the window chronologically. -
range:
currentrestricts the window to a single day with no aggregation across days. -
semiadditive:
lastreturns the most recent balance when aggregating over multiple days.
Query a window measure
You can query a metric view with a window measure like any other metric view. The following example queries a metric view:
SELECT
state,
DATE_TRUNC('month', date),
MEASURE(t7d_customers) as m
FROM my_metric_view
WHERE date >= DATE'2024-06-01'
GROUP BY ALL
Composability
Metric views are composable. You can build new dimensions and measures that reference existing ones rather than rewriting logic from scratch. This reduces duplication and makes complex metric definitions easier to maintain.
Composability works at two levels: within a single metric view, and across metric views. A metric view can use another metric view as its source, allowing you to layer definitions and build progressively richer semantic models without duplicating logic.
Composability supports the following reference patterns:
- Earlier dimensions in new dimensions.
- Dimensions and earlier measures in new measures.
- Dimensions from metric views used as source in new dimensions.
- Dimensions and measures from metric views used as source in new measures.
Define measures with composability
In the measures section, you can reference measures from the source metric view or measures defined earlier in the same metric view. This approach improves consistency, auditability, and maintenance of your semantic layer.
Measure type | Description | Example |
|---|---|---|
Atomic | A simple, direct aggregation on a source column. These form the building blocks. |
|
Composed | An expression that mathematically combines one or more other measures using the |
|
Example: Average Order Value (AOV)
The following example defines Average Order Value (AOV) using two atomic measures: total_revenue (sum of order prices) and order_count (number of orders). The avg_order_value measure references both atomic measures.
version: 1.1
source: samples.tpch.orders
measures:
# Total Revenue
- name: total_revenue
expr: SUM(o_totalprice)
# Order Count
- name: order_count
expr: COUNT(1)
# Composed Measure: Average Order Value (AOV)
- name: avg_order_value
# Defines AOV as Total Revenue divided by Order Count
expr: MEASURE(total_revenue) / MEASURE(order_count)
If the total_revenue definition changes (for example, to exclude tax), avg_order_value automatically uses the updated definition.
Composability with conditional logic
You can use composability to create complex ratios, conditional percentages, and growth rates without relying on window functions for simple period-over-period calculations.
Example: Fulfillment Rate
The following example calculates fulfillment rate: the percentage of orders with status 'F' (fulfilled). The measure divides fulfilled orders by total orders.
version: 1.1
source: samples.tpch.orders
measures:
# Total Orders (denominator)
- name: total_orders
expr: COUNT(1)
# Fulfilled Orders (numerator)
- name: fulfilled_orders
expr: COUNT(1) FILTER (WHERE o_orderstatus = 'F')
# Composed Measure: Fulfillment Rate (Ratio)
- name: fulfillment_rate
expr: MEASURE(fulfilled_orders) / MEASURE(total_orders)
format:
type: percentage
Best practices for composability
- Define atomic measures first: Establish fundamental measures (
SUM,COUNT,AVG) before defining measures that reference them. - Use
MEASURE()for references: Use theMEASURE()function when referencing another measure in anexpr. Don't repeat aggregation logic manually. For example, avoidSUM(a) / COUNT(b)if measures for both values already exist. - Prioritize readability: Compose measures using clear mathematical formulas. For example,
MEASURE(gross_profit) / MEASURE(total_revenue)is clearer than a single complex SQL expression. - Add semantic metadata: Use semantic metadata to format composed measures (for example, percentages or currency) for downstream tools. See Agent metadata in metric views.