MySQL CREATE INDEX: Syntax and Examples
Learn MySQL CREATE INDEX syntax, choose indexed columns, create unique indexes, inspect query plans, and resolve Error 1061 for duplicate index names.
Indexes can improve the speed of data retrieval from a table. In a MySQL database, you can add an index to a table using the CREATE INDEX statement.
For a table with a large number of rows, if it is slow to retrieve data based on a certain query condition, it may be because you have not created an index on the column related to the query condition.
An index is similar to a table of contents in a dictionary. If you want to look up a word in the dictionary, the correct way is to check the table of contents first, and then follow the instructions to find the relevant word on the specified page. Proper indexes can significantly increase the speed of retrieving rows from database tables.
MySQL allows you to create indexes on specified columns in a specified table using the CREATE INDEX statement.
Introduction
An index is a data structure, such as a B-Tree, that increases the speed of data retrieval from a table, but requires additional writes and storage to maintain it.
The query optimizer can use indexes to quickly locate data without having to scan all rows in the table for a given query.
When you declare a primary key or unique key in CREATE TABLE, MySQL creates the corresponding index. The primary-key index is named PRIMARY; a unique key has its own index name.
The clustered-index description applies specifically to InnoDB. InnoDB uses the primary key as its clustered index, which stores the row data. If a table has no primary key, InnoDB uses its first UNIQUE index whose key columns are all NOT NULL; if it has no such index, InnoDB creates a hidden clustered index. Other storage engines have different layouts; for example, MyISAM does not support clustered indexes. See the InnoDB clustered-index documentation and MyISAM feature list.
In InnoDB, indexes other than the clustered index are secondary indexes. Each secondary-index record also stores the table’s primary-key columns, which InnoDB uses to find the row in the clustered index.
MySQL CREATE INDEX syntax
You should use the following syntax of CREATE INDEX to add an index to a table:
CREATE [UNIQUE] INDEX index_name
[USING {BTREE | HASH}]
ON table_name (column_list)
[algorithm_option | lock_option];
Explanation:
-
The
UNIQUEkeyword indicates that this index is a unique index. It is optional. -
index_nameis the name of the index. There should not be two indexes with the same name in a table.
If MySQL returns Error 1061 while creating an index, inspect the existing names and definitions with SHOW INDEX before repeating the statement. See MySQL Error 1061 troubleshooting.
-
table_nameis the name of the table. -
column_listis the column names in the table. Multiple column names are separated by commas. -
The
USINGclause specifies the type of index. Possible values:BTREE,HASH. It is optional. -
algorithm_optionspecifies the algorithm for creating indexes. It uses the following syntax:ALGORITHM [=] {DEFAULT | INPLACE | COPY}In MySQL 9.7, the
ALGORITHMclause acceptsDEFAULT,INPLACE, orCOPY;INSTANTis not anALGORITHMvalue forCREATE INDEX. When the clause is omitted or set toDEFAULT, MySQL selects an algorithm supported by the storage engine and operation. For InnoDB, adding a secondary index is an in-place operation that permits concurrent DML, but it is not instant. IfINPLACEis unavailable for an operation, MySQL can useCOPY. Check the online DDL support for your table engine and server version before relying on a particular algorithm or lock level. See the MySQL 9.7CREATE INDEXreference and InnoDB index-operation matrix.Using
DEFAULTand omitting theALGORITHMclause has the same effect.The following is a description of each algorithm:
COPY: Operate on the copy of the original table, and copy the table data in the original table to the new table row by row. Concurrent DML is not allowed.INPLACE: The operation avoids copying table data, but may rebuild the table in-place. Exclusive metadata locks on tables may be briefly held during the preparation and execution phases of an operation. In general, concurrent DML is supported.
-
lock_optionSpecifies the concurrency control strategy for creating indexes. It uses the following syntax:LOCK [=] {DEFAULT | NONE | SHARED | EXCLUSIVE}LOCKclause is optional. The following are descriptions of each concurrency strategy:DEFAULT-
The maximum concurrency level for the given
ALGORITHMclause (if any) andALTER TABLEoperation: Concurrent reads and writes are allowed, if supported. If not, concurrent reads are allowed (if supported). If not, exclusive access is enforced. NONE-
Allows concurrent reads and writes if supported. Otherwise, an error will occur.
SHARED-
If supported, allow concurrent reads but block writes. Writes are blocked even if the storage engine supports concurrent writes for the given
ALGORITHMclause (if any) and operation.ALTER TABLEAn error occurs if concurrent reads are not supported. EXCLUSIVE-
Enforce exclusive access. This is done even if the storage engine supports concurrent read/write for the given
ALGORITHMclause (if any) and operation.ALTER TABLE
Internally in MySQL, CREATE INDEX statements are mapped to ALTER TABLE ... ADD INDEX ... statements.
MySQL index types
By default, if you do not specify an index type, MySQL will create a B-Tree index. The following shows the index types allowed for table-based storage engines:
| storage engine | allowed index types |
|---|---|
| InnoDB | BTREE |
| MyISAM | BTREE |
| MEMORY/HEAP | HASH,BTREE |
MySQL CREATE INDEX instance
In the following examples, we use the actor table from the Sakila sample database for demonstration.
The following statement finds an actor with first name NICK:
SELECT * FROM sakila.actor WHERE first_name = 'NICK';
Here’s the output:
+----------+------------+-----------+---------------------+
| actor_id | first_name | last_name | last_update |
+----------+------------+-----------+---------------------+
| 2 | NICK | WAHLBERG | 2006-02-15 04:34:33 |
| 44 | NICK | STALLONE | 2006-02-15 04:34:33 |
| 166 | NICK | DEGENERES | 2006-02-15 04:34:33 |
+----------+------------+-----------+---------------------+You can use EXPLAIN to view the execution plan of the above SELECT statement to understand how MySQL executes this query internally, as follows:
For a detailed guide to estimated plans and actual execution timings, see MySQL EXPLAIN and EXPLAIN ANALYZE.
EXPLAIN SELECT * FROM sakila.actor WHERE first_name = 'NICK';
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
| 1 | SIMPLE | actor | NULL | ALL | NULL | NULL | NULL | NULL | 201 | 10.00 | Using where |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+As you can see, MySQL has to scan the entire table consisting of 201 rows to find a row with a matching condition.
Now, let’s create an index CREATE INDEX on this first_name column using the following statement:
CREATE INDEX first_name ON actor(first_name);
Here, we have created an index on the actor table for the first_name column with the name first_name.
To see whether the index was created successfully, use the following SHOW INDEXES statement display actor the index of the table, for example:
SHOW INDEXES FROM actor;
Here’s the output:
+-------+------------+---------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Visible | Expression |
+-------+------------+---------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| actor | 0 | PRIMARY | 1 | actor_id | A | 201 | NULL | NULL | | BTREE | | | YES | NULL |
| actor | 1 | idx_actor_last_name | 1 | last_name | A | 122 | NULL | NULL | | BTREE | | | YES | NULL |
| actor | 1 | first_name | 1 | first_name | A | 129 | NULL | NULL | | BTREE | | | YES | NULL |
+-------+------------+---------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+Then, execute the above EXPLAIN statement:
EXPLAIN SELECT * FROM sakila.actor WHERE first_name = 'NICK';
The output is:
+----+-------------+-------+------------+------+---------------+------------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------------+---------+-------+------+----------+-------+
| 1 | SIMPLE | actor | NULL | ref | first_name | first_name | 182 | const | 3 | 100.00 | NULL |
+----+-------------+-------+------------+------+---------------+------------+---------+-------+------+----------+-------+As you can see, MySQL only needs to locate 3 of the rows from the first_name indicated index without scanning the entire table.
Conclusion
In MySQL, indexes can improve the efficiency of querying data from tables. You can create indexes for tables using CREATE INDEX.