> ## Documentation Index
> Fetch the complete documentation index at: https://cockroachlabs.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Indexes

export const InternalLink = ({version, path = "", children, ...props}) => {
  let detectedVersion = version || "stable";
  if (typeof window !== 'undefined' && !version) {
    const match = window.location.pathname.match(/\/docs\/([^/]+)/);
    if (match) {
      detectedVersion = match[1];
    }
  }
  const normalizedPath = path.startsWith("/") ? path.slice(1) : path;
  return <a href={`/docs/${detectedVersion}/${normalizedPath}`} {...props}>
      {children}
    </a>;
};

Indexes improve your database's performance by helping SQL locate data without having to look through every row of a table.

## How do indexes work?

When you create an index, CockroachDB "indexes" the columns you specify, which creates a copy of the columns and then sorts their values (without sorting the values in the table itself).

After a column is indexed, SQL can easily filter its values using the index instead of scanning each row one-by-one. On large tables, this greatly reduces the number of rows SQL has to use, executing queries exponentially faster.

For example, if you index an `INT` column and then filter it `WHERE <indexed column> = 10`, SQL can use the index to find values starting at 10 but less than 11. In contrast, without an index, SQL would have to evaluate **every** row in the table for values equaling 10. This is also known as a "full table scan", and it can be very bad for query performance.

You can also create an index on a subset of rows. This type of index is called a *partial index*. For more information, see <InternalLink path="partial-indexes">Partial Indexes</InternalLink>.

To index <InternalLink path="export-spatial-data">spatial data</InternalLink>, CockroachDB uses *spatial indexes*. For more information, see <InternalLink path="spatial-indexes">Spatial Indexes</InternalLink>.

In <InternalLink path="multiregion-overview">multi-region deployments</InternalLink>, most users should use <InternalLink path="table-localities#regional-by-row-tables">`REGIONAL BY ROW` tables</InternalLink> instead of explicit index <InternalLink path="partitioning">partitioning</InternalLink>. When you add an index to a `REGIONAL BY ROW` table, it is automatically partitioned on the <InternalLink path="alter-table">`crdb_region` column</InternalLink>. Explicit index partitioning is not required.

While CockroachDB process an <InternalLink path="alter-database#add-region">`ADD REGION`</InternalLink> or <InternalLink path="alter-database#drop-region">`DROP REGION`</InternalLink> statement on a particular database, creating or modifying an index will throw an error. Similarly, all <InternalLink path="alter-database#add-region">`ADD REGION`</InternalLink> and <InternalLink path="alter-database#drop-region">`DROP REGION`</InternalLink> statements will be blocked while an index is being modified on a `REGIONAL BY ROW` table within the same database.

### Creation

Each table automatically has a *primary index* called `{tbl}_pkey`, which indexes either its <InternalLink path="primary-key">primary key</InternalLink> or—if there is no primary key—a unique value for each row known as `rowid`. We recommend always defining a primary key because the index it creates provides much better performance than letting CockroachDB use `rowid`.

To require an explicitly defined primary key for all tables created in your cluster, set the `sql.defaults.require_explicit_primary_keys.enabled` <InternalLink path="cluster-settings">cluster setting</InternalLink> to `true`.

Use <InternalLink path="alter-role#set-default-session-variable-values-for-all-users">`ALTER ROLE ALL SET &#123;sessionvar&#125; = &#123;val&#125;
  `</InternalLink> instead of the `sql.defaults.*` <InternalLink path="cluster-settings">cluster
settings</InternalLink>. This allows you to set a default value for all users for any <InternalLink path="set-vars">session
variable</InternalLink> that applies during login, making the `sql.defaults.*` cluster settings redundant.
The primary index helps filter a table's primary key but doesn't help SQL find values in any other columns. However, you can use <InternalLink path="schema-design-indexes">secondary indexes</InternalLink> to improve the performance of queries using columns not in a table's primary key. You can create them:

<a id="unique-secondary-indexes" />

* At the same time as the table with the `INDEX` clause of <InternalLink path="create-table#create-a-table-with-secondary-and-gin-indexes">`CREATE TABLE`</InternalLink>. In addition to explicitly defined indexes, CockroachDB automatically creates secondary indexes for columns with the <InternalLink path="unique">`UNIQUE` constraint</InternalLink>.
* For existing tables with <InternalLink path="create-index">`CREATE INDEX`</InternalLink>.
* By applying the `UNIQUE` constraint to columns with <InternalLink path="alter-table">`ALTER TABLE`</InternalLink>, which automatically creates an index of the constrained columns.

To review guidelines for creating the most useful secondary indexes, see <InternalLink path="schema-design-indexes#best-practices">Secondary Indexes: Best practices</InternalLink>.

### Selection

In most cases CockroachDB selects the index it calculates will scan the fewest rows (i.e., the fastest). Cases where CockroachDB will use multiple indexes include certain queries that use disjunctions (i.e., predicates with `OR`), as well as <InternalLink path="cost-based-optimizer#zigzag-joins">zigzag joins</InternalLink> for some other queries. To learn how to use the <InternalLink path="explain">`EXPLAIN`</InternalLink> statement for your query to see which index is being used, see [Index Selection in CockroachDB](https://www.cockroachlabs.com/blog/index-selection-cockroachdb-2).

To override CockroachDB index selection, you can also force queries <InternalLink path="table-expressions#force-index-selection">to use a specific index</InternalLink> (also known as "index hinting"). Index hinting is supported for <InternalLink path="select-clause#select-from-a-specific-index">`SELECT`</InternalLink>, <InternalLink path="delete#force-index-selection-for-deletes">`DELETE`</InternalLink>, and <InternalLink path="update#force-index-selection-for-updates">`UPDATE`</InternalLink> statements.

### Storage

CockroachDB stores indexes directly in its key-value store. You can find more information in our blog post [Mapping Table Data to Key-Value Storage](https://www.cockroachlabs.com/blog/sql-in-cockroachdb-mapping-table-data-to-key-value-storage).

### Locking

Tables are not locked during index creation due to CockroachDB support for <InternalLink path="online-schema-changes">online schema changes</InternalLink>.

### Performance

Indexes create a trade-off: they greatly improve the speed of queries, but may slightly slow down writes to an affected column (because new values have to be written for both the table *and* the index).

To maximize your indexes' performance, Cockroach Labs recommends following the <InternalLink path="schema-design-indexes#best-practices">secondary index best practices</InternalLink>.

To observe the impact of an index without affecting a production workload, you can <InternalLink path="create-index">create an index</InternalLink> using the `NOT VISIBLE` clause. If an index is `NOT VISIBLE`, queries will not read from the index unless it is specifically selected with an <InternalLink path="indexes#selection">index hint</InternalLink> or the property is overridden with the <InternalLink path="set-vars">`optimizer_use_not_visible_indexes` session variable</InternalLink>. For an example, refer to <InternalLink path="alter-index#set-an-index-to-be-not-visible">Set an index to be not visible</InternalLink>.

For more index visibility considerations, refer to <InternalLink path="alter-index#not-visible">`[NOT] VISIBLE`</InternalLink>.

We **strongly recommend** adding size limits to all <InternalLink path="indexes">indexed columns</InternalLink>, which includes columns in <InternalLink path="primary-key">primary keys</InternalLink>.

Values exceeding 1 MiB can lead to <InternalLink path="architecture/storage-layer#write-amplification">storage layer write amplification</InternalLink> and cause significant performance degradation or even <InternalLink path="cluster-setup-troubleshooting#out-of-memory-oom-crash">crashes due to OOMs (out of memory errors)</InternalLink>.

To add a size limit using <InternalLink path="create-table">`CREATE TABLE`</InternalLink>:

```sql theme={"theme":{"light":"catppuccin-mocha","dark":"catppuccin-mocha"}}
CREATE TABLE name (first STRING(100), last STRING(100));
```

To add a size limit using <InternalLink path="alter-table#alter-column">`ALTER TABLE... ALTER COLUMN`</InternalLink>:

```sql theme={"theme":{"light":"catppuccin-mocha","dark":"catppuccin-mocha"}}
ALTER TABLE name ALTER first TYPE STRING(99);
```

For more information about how to tune CockroachDB performance, see <InternalLink path="performance-best-practices-overview">SQL Performance Best
Practices</InternalLink>.

### Storing columns

The `STORING` clause specifies columns which are not part of the index key but should be stored in the index. This optimizes queries that retrieve those columns without filtering on them, because it prevents the need to read the <InternalLink path="primary-key">primary index</InternalLink>.

An index that stores all the columns needed by a query is also known as a *covering index* for that query. When a query has a covering index, CockroachDB can use that index directly instead of doing an "index join" with the primary index, which is likely to be slower.

The synonym `COVERING` is also supported.

<a id="locality-example" />

#### Example

Suppose you have a table with three columns, two of which are indexed:

```sql theme={"theme":{"light":"catppuccin-mocha","dark":"catppuccin-mocha"}}
> CREATE TABLE tbl (col1 INT, col2 INT, col3 INT, INDEX (col1, col2));
```

If you filter on the indexed columns but retrieve the unindexed column, this requires reading `col3` from the primary index via an "index join."

```sql theme={"theme":{"light":"catppuccin-mocha","dark":"catppuccin-mocha"}}
> EXPLAIN SELECT col3 FROM tbl WHERE col1 = 10 AND col2 > 1;
```

```text theme={"theme":{"light":"catppuccin-mocha","dark":"catppuccin-mocha"}}
  distribution: local
  vectorized: true

  • index join
  │ table: tbl@tbl_pkey
  │
  └── • scan
        missing stats
        table: tbl@tbl_col1_col2_idx
        spans: [/10/2 - /10]

  index recommendations: 1
  1. type: index replacement
     SQL commands: CREATE INDEX ON tbl (col1, col2) STORING (col3); DROP INDEX tbl@tbl_col1_col2_idx;
(14 rows)
```

However, if you store `col3` in the index as shown in the <InternalLink path="explain#default-statement-plans">index recommendation</InternalLink>, the index join is no longer necessary. This means your query only needs to read from the secondary index, so it will be more efficient.

```sql theme={"theme":{"light":"catppuccin-mocha","dark":"catppuccin-mocha"}}
> CREATE TABLE tbl (col1 INT, col2 INT, col3 INT, INDEX (col1, col2) STORING (col3));
```

```sql theme={"theme":{"light":"catppuccin-mocha","dark":"catppuccin-mocha"}}
> EXPLAIN SELECT col3 FROM tbl WHERE col1 = 10 AND col2 > 1;
```

```text theme={"theme":{"light":"catppuccin-mocha","dark":"catppuccin-mocha"}}
               info
----------------------------------
  distribution: local
  vectorized: true

  • scan
    missing stats
    table: tbl@tbl_col1_col2_idx
    spans: [/10/2 - /10]
(7 rows)
```

## Best practices

For best practices, see <InternalLink path="schema-design-indexes#best-practices">Secondary Indexes: Best practices</InternalLink>.

## Indexes on `REGIONAL BY ROW` tables in multi-region databases

In <InternalLink path="multiregion-overview">multi-region deployments</InternalLink>, most users should use <InternalLink path="table-localities#regional-by-row-tables">`REGIONAL BY ROW` tables</InternalLink> instead of explicit index <InternalLink path="partitioning">partitioning</InternalLink>. When you add an index to a `REGIONAL BY ROW` table, it is automatically partitioned on the <InternalLink path="alter-table">`crdb_region` column</InternalLink>. Explicit index partitioning is not required.

While CockroachDB process an <InternalLink path="alter-database#add-region">`ADD REGION`</InternalLink> or <InternalLink path="alter-database#drop-region">`DROP REGION`</InternalLink> statement on a particular database, creating or modifying an index will throw an error. Similarly, all <InternalLink path="alter-database#add-region">`ADD REGION`</InternalLink> and <InternalLink path="alter-database#drop-region">`DROP REGION`</InternalLink> statements will be blocked while an index is being modified on a `REGIONAL BY ROW` table within the same database.

This behavior also applies to <InternalLink path="inverted-indexes">GIN indexes</InternalLink>.

For an example that uses unique indexes but applies to all indexes on `REGIONAL BY ROW` tables, see <InternalLink path="alter-table#add-a-unique-index-to-a-regional-by-row-table">Add a unique index to a `REGIONAL BY ROW` table</InternalLink>.

## See also

* <InternalLink path="create-index">`CREATE INDEX`</InternalLink>
* <InternalLink path="schema-design-indexes">Secondary Indexes</InternalLink>
* <InternalLink path="inverted-indexes">GIN Indexes</InternalLink>
* <InternalLink path="partial-indexes">Partial Indexes</InternalLink>
* <InternalLink path="spatial-indexes">Spatial Indexes</InternalLink>
* <InternalLink path="vector-indexes">Vector Indexes</InternalLink>
* <InternalLink path="hash-sharded-indexes">Hash-sharded Indexes</InternalLink>
* <InternalLink path="expression-indexes">Expression Indexes</InternalLink>
* <InternalLink path="select-clause#select-from-a-specific-index">Select from a specific index</InternalLink>
* <InternalLink path="drop-index">`DROP INDEX`</InternalLink>
* <InternalLink path="alter-index#rename-to">`ALTER INDEX... RENAME TO`</InternalLink>
* <InternalLink path="show-index">`SHOW INDEX`</InternalLink>
* <InternalLink path="sql-statements">SQL Statements</InternalLink>
