--- title: "Indexing and data skipping" slug: "dev-database-guides-indexing-and-data-skipping" updated: 2026-10-30T18:19:11Z published: 2026-10-30T18:19:11Z canonical: "docs.vastdata.com/dev-database-guides-indexing-and-data-skipping" --- > ## Documentation Index > Fetch the complete documentation index at: https://docs.vastdata.com/llms.txt > Use this file to discover all available pages before exploring further. # Indexing and data skipping *Applies to VAST Cluster 5.5.* VAST DataBase has no B-tree indexes. A query gets faster by reading less: the cluster skips the parts of a table that can't match the filter, and the table's structure decides how much it can skip. This page lists each mechanism, what it speeds up and what it costs, and links to the page with the detail. ## No secondary indexes VAST Cluster 5.5 has no secondary indexes and no `CREATE INDEX` statement. The Query Engine's DDL has none ([SQL reference](/develop/docs/dev-database-reference-sql-index#statements)), and the Python SDK's only index is the vector index you set at `create_table()` ([Python SDK reference](/develop/docs/dev-database-reference-sdk#vector-index)). A `SECONDARY INDEX` or `INCLUDE` clause fails with a parser error ([Vector Search errors](/develop/docs/dev-vector-search-troubleshooting-errors)), and `CREATE INDEX` in the Query Engine fails with `Not implemented Error: not supported`. To look up rows by a column, filter on it, and choose a table structure that orders the data by it. ## The mechanisms | Mechanism | What it speeds up | How you get it | What it costs | Detail | | --- | --- | --- | --- | --- | | Per-chunk min/max statistics | Any filter on a column whose values are ordered or clustered in the table | Nothing to do: every table has them, for numbers, decimals, strings, booleans, dates, times and timestamps | Nothing. They skip little when values are in no order, and nothing for nested types or strings that share a long prefix | [Read path and pruning](/develop/docs/dev-database-concepts-architecture-read-path-and-pruning#skipping-data-with-minmax-statistics) | | Sorted table | Filters on the sort columns | `sorting_key=` at creation, `add_sorting_key()`, Trino `sorted_by` or Query Engine `SORT KEY` | Background sorting; sort columns can't be updated; permanent; no projections, user-defined row IDs, vector index or replication | [Data modeling](/develop/docs/dev-database-guides-data-modeling#sorted-tables) | | Partitioned table | Filters on the partition columns (partition pruning); whole-partition deletes | `partition_spec=` or Trino `partitioning`, at creation only | Partition columns fixed; no projections, user-defined row IDs, vector index or replication; not readable by the Query Engine | [Data modeling](/develop/docs/dev-database-guides-data-modeling#partitioned-tables) | | Semi-sorted projection | Filters on a projection's sort columns, in queries that use only the projection's columns | `create_projection()` in the Python SDK, on an existing regular table | A copy of its columns to store and keep up to date; not replicated; not on sorted or partitioned tables; limits every cell in the table to the pre-5.5 cell size | [Projections](/develop/docs/dev-database-guides-projections) | | Vector index | Nearest-neighbour search on one vector column | `VECTOR CLUSTER BY` in `CREATE TABLE`, or `vector_index=` at creation | Permanent; one per table; no column changes; no sorting, partitioning or user-defined row IDs | [The vector index](/develop/docs/dev-vector-search-concepts-index) | Which of these you can combine is in [limits](/develop/docs/dev-database-reference-limits#combining-table-structures). ## Min/max statistics For each chunk of each column, the cluster keeps the minimum, the maximum and whether it has nulls. Before reading a chunk, it checks your condition against them, and skips the chunk if no row in it can match ([read path and pruning](/develop/docs/dev-database-concepts-architecture-read-path-and-pruning#skipping-data-with-minmax-statistics), [how tables are stored](/develop/docs/dev-database-concepts-architecture-element-store-layout)). So the same filter is cheap when the rows are ordered by the filtered column and expensive when they aren't: a selective filter on unordered data can still read the whole column ([why data order decides the cost](/develop/docs/dev-database-concepts-architecture-read-path-and-pruning#why-data-order-decides-the-cost)). On a regular table of a few million rows, a narrow filter on a column whose values rose with insert order ran much faster than an equally selective filter on a column in random order. The structures below exist to put the rows in an order that lets the statistics skip more. Store timestamps and numbers as their own types, not as strings, or the statistics can't tell values apart. ## Sorted tables A sorted table keeps its rows ordered by up to four sort columns, sorted in the background. A filter on the sort columns lets the cluster skip whole sorted runs, and narrow min/max ranges inside the rest skip most chunks. Recently inserted rows that haven't been sorted yet are always read ([read path and pruning](/develop/docs/dev-database-concepts-architecture-read-path-and-pruning#sorted-tables)). With the same rows, a filter on the sort column of a sorted table ran clearly faster than on a regular table, and the gap grew for a range of values. Filter on the sort columns, and don't OR them with a non-sort column. (The Python SDK can't send an OR across columns at all: it raises `NotImplementedError`.) Small tables aren't effectively sorted ([table structures](/develop/docs/dev-database-concepts-architecture-table-structures#sorted-tables)). ## Partition pruning On a partitioned table, the client first finds the partitions that match the filter's conditions on partition columns, then queries only those. Partitions that can't match are never read. A filter that doesn't mention a partition column scans every partition ([read path and pruning](/develop/docs/dev-database-concepts-architecture-read-path-and-pruning#partitioned-tables), [querying](/develop/docs/dev-database-guides-querying#partitioned-and-sorted-tables)). ## Semi-sorted projections A projection is a copy of some of a table's columns, sorted by up to four of them a block of rows at a time. A query that qualifies still visits every block, but within each block the min/max statistics skip most chunks. A table can have several projections with different sort orders, so they suit filters on different columns ([projections](/develop/docs/dev-database-guides-projections)). A query that asks for a column outside the projection reads the table instead, and was as slow as one with projections turned off. ## The vector index A vector index arranges a table's rows by vector distance on one vector column, so a search opens only the clusters nearest the query vector. A search uses it only when it has the index's query shape; `EXPLAIN` shows `VAST_VEC_INDEX_SCAN` when it does ([the vector index](/develop/docs/dev-vector-search-concepts-index#rules-for-a-search-that-uses-the-index)). Filters in `WHERE` work alongside the search, but they're filters, not an index ([filters](/develop/docs/dev-vector-search-guides-filters)). ## Check what skipping buys you The Python SDK doesn't report how many rows a query read, so compare timings ([profile a query](/develop/docs/dev-database-guides-profile-a-query#time-the-query)): - Time the query on your table, then the same query with the filter on a column in random order, or with `QueryConfig(use_semi_sorted_projections=False)` for a projection. Run each several times, interleaved, and compare the medians. A small table returns too fast to show the difference. - Ask for the same columns each time: fewer columns read is a saving of its own ([optimize queries](/develop/docs/dev-database-guides-optimize-queries#project-only-the-columns-you-need)). - For a sorted table, check `table.sorting_done()` first; rows that aren't sorted yet are always read. ## See also - [Optimize queries](/develop/docs/dev-database-guides-optimize-queries): the checklist. - [Data modeling](/develop/docs/dev-database-guides-data-modeling): choosing a structure before you create the table. - [Table structures](/develop/docs/dev-database-concepts-architecture-table-structures): what each layout buys and costs. - [Limits](/develop/docs/dev-database-reference-limits): the combinability matrix.