Skip to main content

MetricFlow time spine

It's common in analytics engineering to have a date dimension or "time-spine" table as a base table for different types of time-based joins and aggregations. The structure of this table is typically a base column of daily or hourly dates, with additional columns for other time grains, like fiscal quarters, defined based on the base column. You can join other tables to the time spine on the base column to calculate metrics like revenue at a point in time, or to aggregate to a specific time grain.

MetricFlow requires you to define a time-spine table as a model-level configuration in the Semantic Layer for time-based joins and aggregations, such as cumulative metrics. This configuration informs dbt which model should be used for time range joins. It is especially useful for cumulative metrics or calculating time-based offsets. The time-spine model is joined to other tables when calculating certain types of metrics or dimensions. MetricFlow will join the time-spine model in the compiled SQL for the following types of metrics and dimensions:

To see the generated SQL for the metric and dimension types that use time-spine joins, refer to the respective documentation or add the compile=True flag when querying the Semantic Layer to return the compiled SQL.

Configuring time-spine in YAML

  • You only need to configure time-spine models that the Semantic Layer should recognize.
  • At a minimum, define a time-spine table for a daily grain.
  • You can optionally define a time-spine table for a different granularity, like hourly.
  • If your organization uses custom calendars (like fiscal years) or custom granularities (like retail_month), you can configure those as well. Learn how to add custom calendars and granularities further down this page to help support specific business needs.
  • Note that if you don’t have a date or calendar model in your project, you'll need to create one.
  • If you're looking to specify the grain of a time dimension so that MetricFlow can transform the underlying column to the required granularity, refer to the Time granularity documentation

If you already have a date dimension or time-spine table in your dbt project, you can point MetricFlow to this table by updating the model configuration to use this table in the Semantic Layer. This is a model-level configuration that tells dbt to use the model for time range joins in the Semantic Layer.

For example, given the following directory structure, you can create two time spine configurations, time_spine_hourly and time_spine_daily. MetricFlow supports granularities ranging from milliseconds to years. Refer to the Dimensions page (time_granularity tab) to find the full list of supported granularities.

tip

Previously, you had to create a model called metricflow_time_spine in your dbt project. Now, if your project already includes a date dimension or time spine table, you can simply configure MetricFlow to use that table by updating the model setting in the Semantic Layer.

If you don’t have a date dimension table, you can still create one by using the following code snippet to build your time spine model.

Time spine directory structureTime spine directory structure

For an example project, refer to our Jaffle shop example.

Understanding time-spine and granularity

The previous configuration demonstrates a time-spine model called time_spine_daily. It sets the time spine configurations under the time_spine key. The standard_granularity_column is the column that maps to one of our standard granularities [list or link to options here]. The grain of this column must be finer or equal in size to the granularity of all custom granularity columns in the same model. In this case, it's hourly. It needs to reference a column defined under the columns key, in this case, date_hour. MetricFlow will use the standard_granularity_column as the join key when joining the time spine table to other source table. Here, the granularity of the standard_granularity_column is set at the column level, in this case, hour.

Additionally, the custom_granularities field, available in dbt v1.9 and higher) lets you specify non-standard time periods like fiscal_year or retail_month that your organization may use.

Creating a time-spine table

If you need to create a time spine table from scratch, you can do so by adding the following code to your dbt project. The example creates a time spine at a daily grain and an hourly grain. A few things to note when creating time spine models:

  • MetricFlow will use the time spine with the largest compatible granularity for a given query to ensure the most efficient query possible. For example, if you have a time spine at a monthly grain, and query a dimension at a monthly grain, MetricFlow will use the monthly time spine. If you only have a daily time spine, MetricFlow will use the daily time spine and date_trunc to month.
  • You can add a time spine for each granularity you intend to use if query efficiency is more important to you than configuration time, or storage constraints. For most engines, the query performance difference should be minimal and transforming your time spine to a coarser grain at query time shouldn't add significant overhead to your queries.
  • We recommend having a time spine at the finest grain used in any of your dimensions to avoid unexpected errors. i.e., if you have dimensions at an hourly grain, you should have a time spine at an hourly grain.

Example time-spine tables

Daily

metricflow_time_spine.sql

Daily (BigQuery)

Use this model if you're using BigQuery. BigQuery supports DATE() instead of TO_DATE():

Hourly

time_spine_hourly.sql
{{
config(
materialized = 'table',
)
}}

with hours as (

{{
dbt.date_spine(
'hour',
"to_date('01/01/2000','mm/dd/yyyy')",
"to_date('01/01/2025','mm/dd/yyyy')"
)
}}

),

final as (
select cast(date_hour as timestamp) as date_hour
from hours
)

select * from final
-- filter the time spine to a specific range
where date_day > dateadd(year, -4, current_timestamp())
and date_hour < dateadd(day, 30, current_timestamp())

Custom calendar

0