Availability: Pre-aggregates are a Beta feature available on Enterprise plans only.
Getting started
Define pre-aggregates in your dbt project and configure scheduling.
Monitoring and debugging
Track materialization status, debug query matching, and view hit/miss stats.
CLI audit
Inspect dashboard coverage from the terminal and gate CI on hit rates.
How it works
Pre-aggregates follow a four-step cycle:- Define — You add a
pre_aggregatesblock to your dbt model YAML, specifying which dimensions and metrics to include. - Materialize — Lightdash runs the aggregation query against your warehouse and stores the results. This happens automatically on compile, on a cron schedule you define, or when you trigger it manually.
- Match — When a user runs a query, Lightdash checks if every requested dimension, metric, and filter is covered by a pre-aggregate.
- Serve — If a match is found, the query is served from the materialized data instead of hitting your warehouse.
Example
Suppose you have anorders table with thousands of rows, and you define a pre-aggregate with dimensions status and metrics total_amount (sum) and order_count (count), with a day granularity on order_date.
Your warehouse data:
Lightdash materializes this into a pre-aggregate:
Now when a user queries “total amount by status, grouped by month”, Lightdash re-aggregates from the daily pre-aggregate instead of scanning the full table:
This works because
sum can be re-aggregated — summing daily sums gives the correct monthly sum.
Query matching
When a user runs a query, Lightdash checks whether a pre-aggregate can serve it first. A pre-aggregate matches when the query fits inside it on each axis:- Fields are available — every dimension, metric, and filter dimension in the query exists somewhere in the pre-aggregate.
- Grain is reachable — if the query uses a time dimension, its granularity is equal or coarser than the pre-aggregate’s, so the rows can be rolled up. Month is a coarser grain than day. Day is a coarser grain than hour.
- Scope is compatible — if the pre-aggregate defines its own
filters, the query includes an equal or narrower filter, so the subset can be filtered from the pre-aggregate base. - Metrics re-aggregate cleanly — all metrics are supported types. No non-additive metrics (like
count distinct,median, etc. which require recalculation per grouping) and nothing resolved at runtime (raw SQL table calculations,sql_filtermetrics, or SQL dependent on Parameters and user attributes). These can’t be faithfully re-computed from stored rows.
Filtered pre-aggregates
A pre-aggregate can define staticfilters so it materializes only a slice of the source data for a common query pattern, such as status = completed or a rolling order_date: inThePast 52 weeks window. A query then matches it only when it carries the same filter or a narrower one — the scope-compatibility rule above.
See Filtered pre-aggregates for the definition syntax and a worked matching example.
Dimensions from joined tables
Pre-aggregates support dimensions from joined tables. Reference them by their full name (for example,customers.first_name) in the dimensions list.
Supported metric types
Pre-aggregates support metrics that can be re-aggregated from pre-computed results:sumcountminmaxaverage
Current limitations
Pre-aggregates support a narrower subset of the Lightdash semantic layer than regular warehouse queries.Not supported
Pre-aggregates do not support:- Personal warehouse connections. Materialization always runs under a single user’s credentials, so warehouse-level access rules are not applied per viewer. If you rely on personal warehouse connections to enforce data access, use results caching instead.
- Parameters — parameter values are picked at query time, so they cannot be resolved during materialization. Queries that use parameters fall back to the warehouse.
- User attributes when referenced from SQL.
required_attributesandany_attributesare still supported throughmaterialization_role. - Custom metrics created in the Explorer
- Custom SQL dimensions created in the Explorer (Custom bin dimensions are supported)
- SQL table calculations (Formula table calculations are supported)
SQL compatibility
sql_filter (and its alias sql_where) runs both at materialization time and at query time on top of the materialized data.
- At materialization time, the filter is evaluated against your warehouse. If the SQL references Parameters or user attributes, the values injected come from the materialization context — you can pin this to a fixed identity or attribute set with
materialization_roleso the materialization captures the rows you need. - At query time, the same filter is re-applied against the materialized data, which is served by DuckDB. If the
sql_filterSQL uses warehouse-specific syntax that DuckDB doesn’t understand, the query will fail to run against the pre-aggregate and fall back to the warehouse.
Metrics that can’t be pre-aggregated
Pre-aggregates do not support metric types that cannot be re-aggregated from pre-computed results. For example, considercount_distinct on a daily pre-aggregate. If the pre-aggregate stores “2 distinct customers on 2024-01-15” and “1 distinct customer on 2024-01-16”, you cannot sum those daily values to get the monthly distinct count, because the same customer can appear on multiple days.
Re-aggregating gives
2 + 1 = 3, but the correct monthly answer is 2 (Alice, Bob). The pre-aggregate no longer knows which customers were counted.
We’re investigating supporting count_distinct through approximation algorithms. Follow this issue for updates.
For similar reasons, the following metric types are also not supported:
sum_distinct,average_distinctmedian,percentilepercent_of_total,percent_of_previousrunning_total- Custom SQL / post-calculation metrics (including many
numbermetrics) — Follow this issue number,string,date,timestamp,boolean