For most of its history Snowflake had no indexes on ordinary tables. It relied on micro-partitions instead: small chunks of a table with the minimum and maximum value of every column recorded, so a query can skip chunks that cannot contain the rows it needs. That works brilliantly for big analytical queries, and less well when you want a handful of rows by customer ID. On 26 October 2026, secondary indexes became generally available for standard tables. This guide explains what they are, how to create one and when they are worth paying for.
How a secondary index works
A secondary index maps the values of its key columns to the micro-partitions that hold them. When a query filters on those columns, Snowflake looks up the matching partitions in the index and reads only those. If every column the query needs is stored in the index, Snowflake can answer from the index alone. Snowflake builds and maintains the index automatically as data changes.
Create your first index
The syntax will look familiar if you have used PostgreSQL or SQL Server. Key columns come first, in order of importance, and INCLUDE adds extra columns that are stored in the index but not used as the main search key.
-- Speed up lookups by customer and date, and cover the columns the app reads
CREATE INDEX idx_orders_customer
ON orders (customer_id, order_date)
INCLUDE (status, amount);
-- Queries like this can now skip most of the table
SELECT order_date, status, amount
FROM orders
WHERE customer_id = 42
AND order_date >= '2026-10-01';
-- List and remove indexes
SHOW INDEXES IN TABLE orders;
DROP INDEX orders.idx_orders_customer;Keys can also be deterministic expressions, such as a cast or a lower-case version of a column. Write your filters with the same expression so the optimizer can match them:
CREATE INDEX idx_customers_email ON customers (LOWER(email));
SELECT customer_id, name
FROM customers
WHERE LOWER(email) = 'ana@example.com';- Key expressions
- An ordered list of columns or deterministic expressions. Put the column you filter on most first, because earlier keys prune more effectively.
- INCLUDE
- Extra columns stored in the index. They help less with filtering, but let the index return results on its own.
- Covering index
- An index that holds every column a query needs, so the base table is not read.
- Changing an index
- There is no ALTER INDEX. Use CREATE OR REPLACE INDEX, or drop it and create it again.
Index, search optimization or clustering?
Snowflake now has three ways to make filters faster. They overlap, so it helps to know what each is for.
| Secondary index | Search optimization | Automatic clustering | |
|---|---|---|---|
| Best for | Equality, ranges and IN lists | Equality on high-cardinality columns, text and geospatial searches | Ranges on the columns the table is sorted by |
| Keys | Columns and deterministic expressions | Columns | Columns and expressions |
| Can answer without reading the table | Yes, when it covers the query | No | The table itself is reorganised |
| Billed as | Search optimization | Search optimization | Automatic clustering |
- 1
Start with clustering for the main access path
If almost every query filters by date, cluster the table by date. One clustering key serves the most common pattern.
- 2
Add an index for the second path
When an app, API or AI agent also looks up rows by customer, order or device ID, an index on that column avoids reclustering the whole table around it.
- 3
Use search optimization for text and many columns
Substring searches, geospatial filters and ad hoc equality filters on many columns remain its job.
- 4
Test with real queries
Compare elapsed time and partitions scanned in the query profile before and after, and keep the index only if it pays for itself.
Limits to know before you start
Works with
Supported- Standard Snowflake tables, more than one index per table
- Equality, inequality, range and IN-list filters
- Deterministic scalar expressions, including casts
Built for selective lookups on large tables.
Does not work with
Not supported- Apache Iceberg tables, views and materialized views
- Queries that use Time Travel
- Tables or columns with row access, masking, aggregation or privacy policies
- Replication, for now
The optimizer also uses at most one index per table in a query, and may skip it.
Costs and permissions
To create an index you need OWNERSHIP of the table and the ADD SEARCH OPTIMIZATION privilege on its schema. Only the table owner's role can drop it. Maintenance credits appear in the search optimization history, where secondary indexes are labelled separately:
GRANT ADD SEARCH OPTIMIZATION ON SCHEMA sales.public TO ROLE app_owner;
-- Credits spent maintaining secondary indexes in the last 30 days
SELECT table_name, SUM(credits_used) AS credits
FROM snowflake.account_usage.search_optimization_history
WHERE index_type = 'Secondary Index'
AND start_time >= DATEADD(day, -30, CURRENT_TIMESTAMP())
GROUP BY table_name
ORDER BY credits DESC;Tables that change constantly cost more to index, because every change has to be reflected in the index. Indexes pay off best on large tables that are read often by selective queries and updated in batches.
Check your understanding
1/3What does INCLUDE add to a Snowflake secondary index?
Included columns make the index "covering", so the base table may not need to be read.
2/3Which table type cannot have a secondary index?
Iceberg tables, views and materialized views are not supported.
3/3How is index maintenance billed?
Look for the Secondary Index type in the search optimization history to track the cost.
You got 0 out of 3.
More on Snowflake: Snowflake October 2026 update, this week's data platform news, the Snowflake cheat sheet and Snowflake vs Databricks for AI agents. New to Snowflake? Start with Getting started with Snowflake.
Sources: Snowflake release note "Secondary indexes (General availability)" (26 October 2026) and the Snowflake documentation on secondary indexes, CREATE INDEX and search optimization. Features roll out by region and edition; check availability in your account.
