Use case
Sometimes you want a measure to behave dynamically: its rolling window — and the prior-period shift you compare it against — should be chosen by the data consumer at query time rather than fixed in the data model. A common example is an embedded dashboard with a “window” dropdown (R3 / R6 / R9 / R12) where picking a value should change the query, not the data model. The window and the shift can’t be passed as query parameters. Bothrolling_window and time_shift are
properties of a measure definition, resolved when the model compiles — and a member
must mean the same thing in every query, otherwise caching, pre-aggregation matching,
and governance break.
The trick is to move the choice into the query instead. A switch
dimension holds the set of allowed windows and acts as the query-time
parameter, and case measures dispatch to the matching rolling logic based
on the selected value. Consumers only ever touch a small, fixed set of members, so
those members stay well-defined and cacheable.
Data modeling
Say you have monthlygross_sales and want trailing 3-, 6-, 9-, and 12-month totals,
each compared to the immediately preceding window of the same length.
The model has four parts:
- A
growth_windowswitchdimension whosevaluesare the selectable windows. This is the query-time parameter. - A
rolling_windowmeasure per window (the current period). - A
time_shiftmeasure per window that shifts the current-period measure back by the window’s length (the prior period). - Four
casemeasures —gross_sales_current,gross_sales_prior,gross_sales_change, andgross_sales_growth_percentage— that dispatch ongrowth_window. Consumers query only these four, regardless of the selected window.
windows, not a new
measure by hand.
Two requirements make
case measures work:- Every
casemeasure needs anelsebranch. It provides the value when the selectedswitchvalue matches nowhenclause. - Always include the
switchdimension (growth_window) in the query. Thecasemeasures — and the calculated measures built on top of them — need it to dispatch. To pin a single window, add a filter on it (see below); don’t rely on the filter alone.
default_filters entry on growth_window. The unless
clause releases the default as soon as the consumer filters on growth_window
explicitly, so a query with no window filter gets the default, and a query that picks a
window gets that one:
Result
Query the view through the SQL API, selectinggrowth_window and the four
consumer measures. Wrap measures in MEASURE() and provide a date range for the rolling
windows.
With no filter on growth_window, the default_filters entry applies the default
window (3m):
Filtering on
growth_window — what a “window” dropdown does — selects that window:
Filtering on all values returns every window side by side, e.g. to render a comparison:
Pre-aggregations
Add arollup over the per-window measures to accelerate these
queries. growth_window is a switch dimension, so it does not
have to be included: pre-aggregation matching
applies the selected value over the rollup scan. Leaving it out also keeps the
rollup small — including it would multiply the rows by every window in the set.
List the per-window measures, not the four case measures: the case measures
are multi-stage and dispatch to the per-window ones, and it’s
those that the rollup has to name to be matched.
In the JavaScript model, reference them through CUBE[...] rather than by name
as strings. Cube resolves member references by recording them as they’re
accessed, so a string never registers as one — the rollup compiles with an empty
measure list and silently matches nothing.
timeDimensions rather
than an inDateRange filter — a filter is matched as a
generic dimension filter, which requires the time dimension to be listed in the
rollup’s dimensions.
Related recipes
- If you need a single fixed window rather than a consumer-selectable one, see
Active users (DAU, WAU, MAU) (fixed
rolling_windowmeasures) and Period-over-period changes (a fixedtime_shiftcomparison). This recipe generalizes both, making the window and shift selectable at query time. - Passing dynamic parameters in a query also lets a consumer choose something at query time, but the choice there is a data value (e.g. a city) injected into a calculation — not a measure behavior (the window length) as it is here.
- Generating the data model dynamically generates a family of members from a list at model-build time; the consumer then picks by choosing which member to query, rather than passing a query-time value.