Index Types
The variety of index data structures available, each optimized for different data types and query patterns.
B-Tree index GIN index GiST index BRIN index Hash index Columnstore index Bitmap index FULLTEXT index SP-GiST
index B-Tree GIN GiST BRIN Hash Columnstore Bitmap
| Database | Status | Syntax Family | Min Version |
|---|---|---|---|
| PostgreSQL | full | using-clause | - |
| MySQL | full | using-clause | - |
| MariaDB | full | using-clause | - |
| Oracle | full | using-clause | - |
| SQL Server | full | using-clause | - |
| SQLite | extension | virtual-table-module | - |
| Redshift | none | - | - |
| DB2 | full | using-clause | - |
| Snowflake | none | - | - |
| BigQuery | partial | - | - |
Compare Databases
Database Details
PostgreSQL
full
-- B-Tree (default): ordered data, range queries, equality
CREATE INDEX idx_btree ON orders (customer_id);
-- Hash: equality-only lookups, smaller than B-Tree for equality
CREATE INDEX idx_hash ON sessions USING hash (session_token);
-- GIN: inverted index for arrays, JSONB, full-text search
CREATE INDEX idx_gin_tags ON posts USING gin (tags);
CREATE INDEX idx_gin_json ON events USING gin (payload jsonb_path_ops);
CREATE INDEX idx_gin_fts ON docs USING gin (to_tsvector('english', body));
-- GiST: geometric types, range types, custom operator classes
CREATE INDEX idx_gist_geo ON locations USING gist (coordinates);
CREATE INDEX idx_gist_range ON reservations USING gist (during);
-- SP-GiST: space-partitioned structures (quadtree, kd-tree, trie)
CREATE INDEX idx_spgist ON points USING spgist (location);
-- BRIN: block range index, tiny footprint for insert-ordered data
CREATE INDEX idx_brin ON logs USING brin (created_at);
CREATE INDEX idx_brin_custom ON logs USING brin (created_at) WITH (pages_per_range = 32);
-- Bloom filter: probabilistic multi-column equality testing
CREATE INDEX idx_bloom ON t USING bloom (col1, col2, col3);- B-Tree is the default and most versatile: supports =, <, >, <=, >=, BETWEEN, IN, IS NULL, LIKE 'prefix%'.
- Hash indexes are smaller and faster for pure equality lookups but do not support range queries or sorting.
- GIN (Generalized Inverted Index) is essential for JSONB containment (@>), array overlap (&&), and full-text search (@@).
- GiST (Generalized Search Tree) is used by PostGIS for spatial queries and supports custom operator classes.
- BRIN stores only the min and max values per range of heap pages, which keeps it very small (hundreds of bytes vs. gigabytes for B-Tree) for monotonically increasing columns like timestamps.
- SP-GiST (Space-Partitioned GiST) supports space-partitioned data structures like quadtrees, kd-trees, and tries.
- The USING clause in CREATE INDEX selects the index type; B-Tree is the default if omitted.
MySQL
full
-- B-Tree (default for InnoDB): equality, range, prefix
CREATE INDEX idx_email ON users (email);
-- Hash: only available for MEMORY storage engine tables
CREATE TABLE cache (
key_col VARCHAR(255) PRIMARY KEY,
value TEXT
) ENGINE=MEMORY;
CREATE INDEX idx_hash ON cache USING HASH (key_col);
-- FULLTEXT: natural language and boolean full-text search (InnoDB, MyISAM)
CREATE FULLTEXT INDEX idx_fts ON articles (title, body);
SELECT * FROM articles WHERE MATCH(title, body) AGAINST ('database indexing' IN BOOLEAN MODE);
-- SPATIAL: for geometry columns (MyISAM and InnoDB with spatial data types)
CREATE SPATIAL INDEX idx_spatial ON locations (coordinates);
-- Multi-value index: index each element of a JSON array (MySQL 8.0.17+)
CREATE INDEX idx_tags ON posts ((CAST(tags->>'$' AS UNSIGNED ARRAY)));- InnoDB uses B-Tree for all regular indexes. The primary key index is a clustered index, so the table data is physically organized by the primary key.
- FULLTEXT indexes support IN NATURAL LANGUAGE MODE (default) and IN BOOLEAN MODE searches.
- Hash indexes are only available for the MEMORY (in-memory) storage engine, not InnoDB.
- SPATIAL indexes require columns with GEOMETRY or sub-type data types and use R-Tree internally.
- Multi-value indexes (MySQL 8.0.17+) index each element of a JSON array, enabling efficient array membership queries.
MariaDB
full
-- B-Tree (InnoDB default)
CREATE INDEX idx_email ON users (email);
-- FULLTEXT
CREATE FULLTEXT INDEX idx_fts ON articles (title, body);
-- SPATIAL (R-Tree)
CREATE SPATIAL INDEX idx_geo ON locations (coordinates);
-- Hash (MEMORY/Aria engines)
CREATE TABLE cache (k VARCHAR(100) PRIMARY KEY) ENGINE=MEMORY;
CREATE INDEX idx_hash ON cache USING HASH (k);
-- Mroonga: Groonga-based full-text search engine for Japanese text
-- (requires Mroonga storage engine plugin)
CREATE TABLE docs (
id INT AUTO_INCREMENT PRIMARY KEY,
body TEXT,
FULLTEXT INDEX (body) COMMENT 'parser "TokenBigram"'
) ENGINE=Mroonga;- MariaDB supports the same base index types as MySQL (B-Tree, FULLTEXT, SPATIAL, Hash for MEMORY engine).
- The Aria storage engine (MariaDB's crash-safe MyISAM replacement) supports B-Tree and FULLTEXT indexes.
- Mroonga is a MariaDB storage engine plugin that integrates the Groonga full-text search engine, offering enhanced Japanese and n-gram tokenization.
- ColumnStore storage engine uses columnar storage with its own internal indexing for analytical workloads.
- Spider storage engine for federated/sharded tables creates indexes on remote nodes.
Oracle
full
-- B-Tree (default)
CREATE INDEX idx_email ON users (email);
-- Bitmap: low-cardinality columns in data warehouses
CREATE BITMAP INDEX idx_status ON orders (status);
-- Function-based: index on an expression
CREATE INDEX idx_email_upper ON users (UPPER(email));
-- Reverse key: prevents right-side contention on sequence-based keys
CREATE INDEX idx_id_reverse ON orders (id) REVERSE;
-- Partitioned local index: one index partition per table partition
CREATE INDEX idx_date ON sales (sale_date) LOCAL;
-- Bitmap join index: pre-join bitmap index across tables
CREATE BITMAP INDEX idx_region
ON sales (customers.region)
FROM sales, customers
WHERE sales.customer_id = customers.id;
-- Oracle Text (full-text) index
CREATE INDEX idx_fts ON articles (body) INDEXTYPE IS CTXSYS.CONTEXT;- Bitmap indexes store a bitstring per distinct value, which makes them very efficient for AND/OR/NOT combinations on low-cardinality columns (status, region, gender) in data warehouse queries.
- Bitmap indexes are not suitable for OLTP; they require row-level locking of the entire bitmap entry on DML, causing severe contention.
- Reverse key indexes byte-reverse the key to distribute sequential inserts across leaf blocks, preventing right-side contention.
- Bitmap join indexes pre-join data from multiple tables into a single bitmap structure, useful for star-schema queries.
- Oracle Text (CTXSYS.CONTEXT) provides full-text indexing with linguistic analysis, thesaurus support, and proximity search.
- IOT (Index-Organized Tables) store all row data in a B-Tree, eliminating the separate heap segment.
SQL Server
full
-- Non-clustered B-Tree (default)
CREATE INDEX idx_email ON users (email);
-- Clustered B-Tree: the table is physically organized by this index
CREATE CLUSTERED INDEX idx_pk ON orders (id);
-- Note: PRIMARY KEY creates a clustered index by default in SQL Server
-- Non-clustered columnstore: add analytical acceleration to an OLTP table
CREATE NONCLUSTERED COLUMNSTORE INDEX idx_cs ON orders (customer_id, order_date, total);
-- Clustered columnstore: pure analytical/DW table (replaces the heap)
CREATE CLUSTERED COLUMNSTORE INDEX idx_cs ON fact_sales;
-- Hash index: only for memory-optimized (In-Memory OLTP) tables
CREATE TABLE hot_sessions (
session_id UNIQUEIDENTIFIER NOT NULL,
data NVARCHAR(MAX),
INDEX idx_hash HASH (session_id) WITH (BUCKET_COUNT = 1000000)
) WITH (MEMORY_OPTIMIZED = ON, DURABILITY = SCHEMA_AND_DATA);
-- Spatial index: for geometry/geography columns
CREATE SPATIAL INDEX idx_geo ON locations (coordinates)
USING GEOMETRY_GRID WITH (BOUNDING_BOX = (0, 0, 100, 100));- Clustered indexes define the physical row order of the table; each table can have only one clustered index (usually the primary key).
- Columnstore indexes compress data column-by-column and process queries in vectorized batches, delivering 10-100x speedup for analytical aggregations (COUNT, SUM, AVG, GROUP BY).
- Non-clustered Columnstore can coexist with B-Tree indexes on the same OLTP table for mixed workloads (Operational Analytics).
- Hash indexes for In-Memory OLTP tables provide O(1) equality lookup with no range scan support.
- Full-Text indexes (CREATE FULLTEXT INDEX) enable linguistic full-text search via CONTAINS() and FREETEXT() predicates.
- XML indexes (PRIMARY XML INDEX, SECONDARY XML INDEX) optimize XQuery and XPath expressions on XML columns.
SQLite
extension
-- B-Tree (only index type for regular tables)
CREATE INDEX idx_email ON users (email);
-- Multi-column B-Tree
CREATE INDEX idx_name ON users (last_name, first_name);
-- R*Tree extension: spatial and multi-dimensional range queries
-- (requires FTS5 / R*Tree module, usually compiled in)
CREATE VIRTUAL TABLE geo_index USING rtree(
id,
min_lat, max_lat,
min_lon, max_lon
);
INSERT INTO geo_index VALUES (1, 37.7, 37.8, -122.5, -122.4);
SELECT id FROM geo_index
WHERE min_lat > 37.6 AND max_lat < 37.9
AND min_lon > -122.6 AND max_lon < -122.3;- SQLite uses B-Tree for all regular indexes; there is no USING clause in CREATE INDEX syntax.
- The R*Tree extension module (usually compiled into standard SQLite distributions) provides multi-dimensional spatial range queries via virtual tables.
- FTS5 (full-text search) is implemented as a virtual table extension: CREATE VIRTUAL TABLE docs USING fts5(title, body).
- SQLite does not have Hash, GIN, BRIN, or other specialized index types.
- All regular indexes, including UNIQUE indexes, use the same B-Tree implementation.
Redshift
none
-- Redshift has no user-defined indexes.
-- Sort keys define physical row order for zone map pruning:
CREATE TABLE orders (
id BIGINT,
customer_id INT,
order_date DATE,
total DECIMAL(10,2)
)
DISTKEY (customer_id)
COMPOUND SORTKEY (customer_id, order_date);
-- Zone maps (automatic): each 1MB disk block stores min/max per column.
-- Queries with WHERE clauses matching the sort key skip irrelevant blocks.
-- Analyze sort key effectiveness:
SELECT tbl, col, sortkey
FROM svv_table_info
WHERE table = 'orders';- Redshift uses zone maps (automatic min/max metadata per ~1MB block per column) for block-level pruning.
- Sort keys (COMPOUND or INTERLEAVED) control physical row ordering; queries aligned with the sort key skip entire block groups.
- Distribution keys (DISTKEY) determine how rows are distributed across compute nodes for join colocation.
- ENCODE (compression encoding per column) is the main storage optimization: AZ64, ZSTD, LZO, BYTEDICT, RUNLENGTH, DELTA.
- No B-Tree, bitmap, hash, or other user-defined index types exist in Redshift.
Docs: Sort Keys, Distribution Styles
DB2
full
-- B-Tree (default)
CREATE INDEX idx_email ON users (email);
-- Bidirectional B-Tree (for DESC and ASC queries on the same column)
CREATE INDEX idx_date_desc ON orders (order_date DESC);
-- Clustering index: data is physically ordered by this index
CREATE INDEX idx_cluster ON orders (customer_id) CLUSTER;
-- MDC (Multi-Dimensional Clustering): physical clustering on multiple columns
-- (defined at table creation or ALTER TABLE)
CREATE TABLE fact_sales (
year_id INT,
region_id INT,
amount DECIMAL(12,2)
) ORGANIZE BY DIMENSIONS (year_id, region_id);
-- Text Search index (requires DB2 Text Search feature)
-- CREATE INDEX idx_fts ON articles (body) FOR TEXT;
-- (CLI: db2ts CREATE INDEX FOR TEXT ON mydb.articles (body))
-- BLU Acceleration: column-organized table (analytical)
CREATE TABLE fact_orders (
id BIGINT,
customer_id INT,
total DECIMAL(12,2)
) ORGANIZE BY COLUMN;- DB2 B-Tree indexes are the standard type for OLTP queries.
- Clustering indexes physically order the table data by the index key, reducing I/O for range scans (one clustering index per table).
- MDC (Multi-Dimensional Clustering) maintains physical clustering on multiple dimension columns simultaneously using extents.
- ORGANIZE BY COLUMN (BLU Acceleration) converts a table to columnar storage, similar to SQL Server Columnstore, for analytical workloads.
- DB2 Text Search uses an Apache Lucene-based index for full-text queries.
- Spatial Extender (optional component) provides R-Tree based spatial indexes for geographic data types.
Snowflake
none
-- Snowflake has no user-defined traditional index types.
-- Micro-partition pruning (automatic zone maps per column per partition):
-- Enabled automatically for all tables.
-- Cluster keys co-locate related rows for better pruning:
ALTER TABLE orders CLUSTER BY (order_date, customer_id);
-- Check clustering depth and effectiveness:
SELECT SYSTEM$CLUSTERING_INFORMATION('orders', '(order_date, customer_id)');
-- Search Optimization Service: accelerates selective equality/range lookups
ALTER TABLE users ADD SEARCH OPTIMIZATION ON EQUALITY(email);
ALTER TABLE docs ADD SEARCH OPTIMIZATION ON SUBSTRING(body);- Snowflake uses automatic micro-partitioning: each ~16MB compressed partition stores min/max metadata per column (zone maps).
- Queries with selective WHERE clauses automatically skip irrelevant micro-partitions without any user-defined indexes.
- Cluster keys instruct Snowflake to physically reorganize micro-partitions by the specified column(s) for better pruning.
- Search Optimization Service is an accelerator for selective equality, range, and substring queries on specific columns.
- No B-Tree, bitmap, hash, GIN, BRIN, or other traditional user-defined index types exist.
BigQuery
partial
-- Partitioned table: time-based row pruning
CREATE TABLE orders
PARTITION BY DATE(order_date)
AS SELECT * FROM staging.orders;
-- Clustered table: physical co-location for column pruning
CREATE TABLE orders
PARTITION BY DATE(order_date)
CLUSTER BY customer_id, status
AS SELECT * FROM staging.orders;
-- Search Index: managed index for SEARCH() function
CREATE SEARCH INDEX idx ON mydataset.orders (ALL COLUMNS);
-- Query using SEARCH index
SELECT * FROM orders
WHERE SEARCH(orders, 'urgent');
-- Check search index status
SELECT * FROM mydataset.INFORMATION_SCHEMA.SEARCH_INDEXES;- BigQuery manages its own columnar internal storage; there are no user-defined B-Tree, bitmap, or hash indexes.
- Partitioned tables restrict query scans to specific time or range partitions, reducing bytes billed.
- Clustered tables physically co-locate rows with similar values in up to 4 clustering columns, enabling block-level pruning.
- Search Indexes (CREATE SEARCH INDEX, GA 2023) support the SEARCH() function for full-text and substring lookups across all or selected columns.
- Vector indexes (CREATE VECTOR INDEX) are available for nearest-neighbor search using the VECTOR_SEARCH() function.
- All traditional index management (vacuum, rebuild, analyze) is handled automatically by BigQuery.