Project: YugabyteDB (App Definition and Development / Database)
File: ddl_create_index.md

title: CREATE INDEX statement [YSQL] headerTitle: CREATE INDEX linkTitle: CREATE INDEX description: Use the CREATE INDEX statement to create an index on the specified columns of the specified table. menu: v2.18: identifier: ddlcreateindex parent: statements type: docs Use the `CREATE INDEX` statement to create an index on the specified columns of the specified table. Indexes are primarily used to improve query performance. {{%ebnf%}} create_index, index_elem {{%/ebnf%}} When an index is created on a populated table, YugabyteDB automatically backfills the existing data into the index. In most cases, this uses an online schema migration. The following table explains some of the differences between creating an index online and not online. | Condition | Online | Not online | | :-- | :-- | : | | Safe to do other DMLs during `CREATE INDEX`? | yes | no | | Keeps other transactions alive during `CREATE INDEX`? | mostly | no | | Parallelizes index loading? | yes | no | `CREATE INDEX CONCURRENTLY` is supported, though online index backfill is enabled by default. Some restrictions apply (see ). To disable online schema migration for YSQL `CREATE INDEX`, set the flag `ysqldisableindex_backfill=true` on all nodes and both YB-Master and YB-TServer. To disable online schema migration for one `CREATE INDEX`, use `CREATE INDEX NONCONCURRENTLY`. {{< note title="Note" >}} For details on how online index backfill works, refer to . {{< /note >}} Regarding colocation, indexes follow their table. If the table is colocated, its index is also colocated; if the table is not colocated, its index is also not colocated. Creating an index on a partitioned table automatically creates a corresponding index for every partition in the default tablespace. It's also possible to create an index on each partition individually, which you should do in the following cases: Parallel writes are expected while creating the index, because concurrent builds for indexes on partitioned tables aren't supported. In this case, it's better to use concurrent builds to create indexes on each partition individually. is being used. In this case, create the index separately on each partition to customize the tablespace in which each index is created. `CREATE INDEX CONCURRENTLY` is not supported for partitioned tables (see ). Enforce that duplicate values in a table are not allowed. Enable online schema migration (see for details), with some restrictions: When creating an index on a temporary table, online schema migration is disabled. `CREATE INDEX CONCURRENTLY` is not supported for partitioned tables. `CREATE INDEX CONCURRENTLY` is not supported inside a transaction block. Disable online schema migration (see for details). Indicates not to recurse creating indexes on partitions, if the table is partitioned. The default is to recurse. The name of the index access method. By default, `lsm` is used for YugabyteDB tables and `btree` is used otherwise (for example, temporary tables). can be created in YugabyteDB by using the `ybgin` access method. Specify a list of columns which will be included in the index as non-key columns. Specify the name of the that describes the placement configuration for this

By default, indexes are placed in the `pg_default` tablespace, which spreads the tablets of the index evenly across the cluster. A is an index that is built on a subset of a table and includes only rows that satisfy the condition specified in the `WHERE` clause. It can be used to exclude NULL or common values from the index, or include just the rows of interest. This speeds up any writes to the table because rows containing the common column values don't need to be indexed. It also reduces the size of the index, thereby improving the speed for read queries that use the index. Specify the name of the index to be created. Specify the name of the table to be indexed. Specify the name of a column of the table. Specify one or more columns of the table and must be surrounded by parentheses. `HASH` - Use hash of the column. This is the default option for the first column and is used to shard the index table. `ASC` Sort in ascending order. This is the default option for second and subsequent columns of the index. `DESC` Sort in descending order. `NULLS FIRST` - Specifies that nulls sort before non-nulls. This is the default when DESC is specified. `NULLS LAST` - Specifies that nulls sort after non-nulls. This is the default when DESC is not specified. For hash-sharded indexes, you can use the `SPLIT INTO` clause to specify the number of tablets to be created for the index. The hash range is then evenly split across those tablets. Presplitting indexes, using `SPLIT INTO`, distributes index workloads on a production cluster. For example, if you have 3 servers, splitting the index into 30 tablets can provide higher write throughput on the index. For an example, see . {{< note title="Note" >}} By default, YugabyteDB presplits an index into `ysqlnumshardspertserver * numoftserver` tablets. The `SPLIT INTO` clause can be used to override that setting on a per-index basis. {{< /note >}} Create a unique index with hash ordered columns. ```plpgsql yugabyte=# CREATE TABLE products(id int PRIMARY KEY, name text, code text); yugabyte=# CREATE UNIQUE INDEX ON products(code); yugabyte=# \d products Table "public.products" Column | Type | Collation | Nullable | Default --++--+-+ id | integer | | not null | name | text | | | code | text | | | Indexes: "products_pkey" PRIMARY KEY, lsm (id HASH) "productscodeidx" UNIQUE, lsm (code HASH) ``` Create an index with ascending ordered key. ```plpgsql yugabyte=# CREATE INDEX products_name ON products(name ASC); yugabyte=# \d products_name Index "public.products_name" Column | Type | Key? | Definition --+++ name | text | yes | name lsm, for table "public.products ``` Create an index with ascending ordered key and include other columns as non-key columns ```plpgsql yugabyte=# CREATE INDEX productsnamecode ON products(name) INCLUDE (code); yugabyte=# \d productsnamecode; Index "public.productsnamecode" Column | Type | Key? | Definition --+++ name | text | yes | name code | text | no | code lsm, for table

``` To specify the number of tablets for an index, you can use the `CREATE INDEX` statement with the clause. ```plpgsql CREATE TABLE employees (id int PRIMARY KEY, firstname TEXT, lastname TEXT) SPLIT INTO 10 TABLETS; CREATE INDEX ON employees(firstname, lastname) SPLIT INTO 10 TABLETS; ``` Consider an application maintaining shipments information. It has a `shipments` table with a column for `delivery_status`. If the application needs to access in-flight shipments frequently, then it can use a partial index to exclude rows whose shipment status is `delivered`. ```plpgsql yugabyte=# create table shipments(id int, deliverystatus text, address text, deliverydate date); yugabyte=# create index shipmentdelivery on shipments(deliverystatus, address, deliverydate) where deliverystatus != 'delivered'; ``` If the following troubleshooting tips don't resolve your issue, ask for help in our or . If online `CREATE INDEX` fails, an invalid index may be left behind. These indexes are not usable in queries and cause internal operations, so they should be dropped. For example, the following commands can create an invalid index: ```plpgsql yugabyte=# CREATE TABLE uniqueerror (i int); ``` ```output CREATE TABLE ``` ```plpgsql yugabyte=# INSERT INTO uniqueerror VALUES (1), (1); ``` ```output INSERT 0 2 ``` ```plpgsql yugabyte=# CREATE UNIQUE INDEX ON uniqueerror (i); ``` ```output ERROR: ERROR: duplicate key value violates unique constraint "uniqueerroriidx" ``` ```plpgsql yugabyte=# \d uniqueerror Table "public.uniqueerror" Column | Type | Collation | Nullable | Default --++--+-+ i | integer | | | Indexes: "uniqueerroriidx" UNIQUE, lsm (i HASH) INVALID ``` Drop the invalid index as follows: ```plpgsql yugabyte=# DROP INDEX uniqueerroriidx; ``` ```output DROP INDEX ``` `ERROR: duplicate key value violates unique constraint "uniqueerroriidx"` Reason: When creating a , a unique constraint violation was found. Fix: Resolve the conflicting row(s). `ERROR: Backfilling indexes { timeoutmasteriidx } for tablet 42e3857759f54733a47e3bb817636f60 from key '' in state kFailed` Reason: Server-side backfill timeout is repeatedly hit. Fixes Do any or all of the following: Increase `ysqlindexbackfillrpctimeout_ms` from 60000 (one minute) to 300000 (five minutes). Increase `backfillindextimeoutgracemargin_ms` from -1 (one second) to 60000 (one minute). Decrease `backfillindexwritebatchsize` from 128 to 32. `ERROR: BackfillIndex RPC (request call id 123) to 127.0.0.1:9100 timed out after 86400.000s` Reason: Client-side backfill timeout is hit. Fixes The master leader may have changed during backfill. This is currently . Retry creating the index, and keep an eye on the master leader. Try increasing parallelism. Index backfill happens in parallel across each tablet of the table. A one-tablet table in an setup would not take advantage of the parallelism. (One-tablet tables are default for range-partitioned tables and colocated tables.) On the other hand, no matter how much parallelism there is, a one-tablet index would be a bottleneck for index backfill writes. Partitioning could be improved with . In case the backfill really needs more time, increase `backfillindexclientrpctimeout_ms` to as long as you expect the backfill to take (for example, one week). To prioritize keeping other transactions alive during the index backfill, set each of the following to be longer than the longest transaction anticipated: `indexbackfillwaitforoldtxnsms` YSQL parameter `ybindexstateflagsupdate_delay` To speed up index creation by a few seconds when you know there will be no online writes, set the YSQL parameter `ybindexstateflagsupdate_delay` to zero.