Overview
A data skipping index lets Firebolt skip granules that are guaranteed to contain no row matching a query’s filter, so they are never read from storage. It is the granule-level pruning step for columns that are not the primary index, stage 3 of the scan pipeline. Firebolt currently supports one kind of data skipping index, theminmax index.
It records the minimum and maximum value of an indexed column (or expression) within each granule (or, with a higher GRANULARITY, each group of granules).
At query time, the engine compares the predicate in your WHERE clause against these precomputed bounds and skips any granule whose [min, max] range cannot contain a matching row.
It is most effective on columns whose values are clustered or roughly ordered within the table, so that filters can rule out large contiguous ranges of granules.
When to use
A
minmax index gives little benefit when the indexed column’s values are randomly distributed, because every granule then tends to span the whole value range and nothing can be skipped.
How it relates to other pruning techniques
Aminmax index is the granule-level pruning step for non-primary-index columns. It runs after the coarser tablet-level steps have chosen which tablets to open, and refines the work within them. For the full ordering of pruning, read, and filter stages, see the scan pipeline; the table below summarizes how a minmax index relates to the other pruning mechanisms.
These mechanisms stack: one query can prune whole tablets from statistics, skip granules via the primary index on one column, and skip more via a
minmax index on another.
Quick start
SQL reference
CREATE INDEX
The keywords
SKIP_INDEX, TYPE, GRANULARITY, and minmax are case-insensitive; this reference uses uppercase for the method and parameter keywords to match the other USING forms.
Both TYPE and GRANULARITY are required; supplying any other set of parameters is an error.
You may create multiple data skipping indexes on the same table, but at most one index per expression.
Each index must have a unique name.
Supported types
The indexed expression must resolve to one of the following comparable types (both nullable and non-nullable variants are allowed):INT, BIGINT, REAL, DOUBLE PRECISION, DECIMAL, DATE, TIMESTAMP, TIMESTAMPTZ, TEXT, BOOLEAN, BYTEA.
Expression-based indexing
You can index any scalar expression, not just a bare column. The expression is evaluated once at ingest time and its min/max are stored. At query time the predicate’s expression must match the indexed expression exactly for pruning to apply:Creating an index on a table that already has data
When you create a data skipping index on a table that already contains data, the existing tablets have no index data and are not pruned until they are rewritten. RunVACUUM with REINDEX=TRUE to backfill the index for pre-existing tablets:
DROP INDEX
VACUUM on the table afterward.
A column that is referenced by a data skipping index cannot be dropped while the index exists:Renaming a column does not require dropping the index.
The index follows the rename automatically.
Capabilities
Supported predicates
The optimizer derives pruning bounds for the common comparison and range predicates. For a predicate on an indexed columnb, a granule is kept only when its [min, max] range could still contain a match:
Multiple predicates combined with
AND each contribute independent pruning conditions.
NULL handling
NULL values never satisfy a comparison predicate.
A granule that contains only NULL values in the indexed column is pruned for predicates such as b > 150, because no non-null value exists that could match.
Coexistence with other index types
Data skipping indexes coexist with all other index types on the same table: primary indexes, aggregating indexes, inverted indexes, and vector search indexes. Each serves a different access pattern, and their pruning combines within a single query. See How it relates to other pruning techniques for how aminmax index fits alongside tablet, primary index, and inverted index pruning.
Choosing a granularity
GRANULARITY controls how many consecutive data granules are summarized by a single index granule (a single min/max pair):
GRANULARITY = 1stores one min/max pair per data granule (~8,192 rows). This gives the finest-grained pruning and the most precise skipping, at the cost of a larger index.- Higher values group more data granules under one min/max pair. The index is smaller, but pruning is coarser: a single matching row anywhere in the group prevents the whole group from being skipped.
GRANULARITY = 1 for the most aggressive pruning.
Increase it only if index size becomes a concern and your filters still prune effectively at a coarser resolution.
The size of a data granule is governed by the table’s
index_granularity (default 8,192 rows).
The data skipping index’s GRANULARITY then sets how many of those granules share one min/max summary.Observability
information_schema.indexes
Useinformation_schema.indexes to list data skipping indexes. They report an index_type of data_skipping:
EXPLAIN (ANALYZE)
UseEXPLAIN (ANALYZE) to confirm the index is used and to see how many granules were pruned. The scan operator gains two annotations and a pruning metric:
If these annotations do not appear, verify that:
- The predicate’s expression matches the indexed expression exactly.
- The table’s tablets contain index data. Run
VACUUM (REINDEX=TRUE)if the index was created after the data was loaded.
How it works
During ingestion, as each data granule is written, Firebolt scans the indexed expression’s values in that granule and records their minimum and maximum. EveryGRANULARITY data granules, these are flushed as one index granule’s min/max pair and stored alongside the tablet’s data.
At query time, the optimizer rewrites each supported WHERE predicate into an equivalent condition over the stored min/max columns (the [DataSkippingConditions] shown in EXPLAIN).
For every tablet the query reads, the storage layer evaluates those conditions per index granule and skips the granules whose [min, max] range provably cannot contain a match.
Only the surviving granules are decompressed and scanned.
Because the conditions only ever exclude granules that cannot contain a matching row, query results are identical with or without the index.
The index changes only how much data is read.