5.5. INDEX
An index is a database object used for faster data retrieval from a table or for speeding up the sorting in a query.
Indexes are also used to enforce the referential integrity constraints PRIMARY KEY, FOREIGN KEY and UNIQUE.
This section describes how to create indexes, activate and deactivate them, drop them and collect statistics (recalculate selectivity) for them.
5.5.1. CREATE INDEX
Creates an index
Available inDSQL, ESQL
Syntax
CREATE [UNIQUE] [ASC[ENDING] | DESC[ENDING]]
INDEX indexname ON tablename
{(col [, col ...]) | COMPUTED BY (<expression>)}
[WHERE <search_condition>]
CREATE INDEX Statement Parameters| Parameter | Description |
|---|---|
indexname | Index name. The maximum length is 63 characters |
tablename | The name of the table for which the index is to be built |
col | Name of a column in the table.
Columns of the types |
expression | The expression that will compute the values for a computed index, also known as an |
search_condition | Conditional expression of a partial index, to filter the rows to include in the index. |
The CREATE INDEX statement creates an index for a table that can be used to speed up searching, sorting and grouping.
Indexes are created automatically in the process of defining constraints, such as primary key, foreign key or unique constraints.
An index can be built on the content of columns of any data type except for BLOB and arrays.
The name (identifier) of an index must be unique among all index names.
5.5.1.1. Who Can Create an Index?
The CREATE INDEX statement can be executed by:
The owner of the table
Users with the
ALTER ANY TABLEprivilege
5.5.1.2. Unique Indexes
Specifying the keyword UNIQUE in the index creation statement creates an index in which uniqueness will be enforced throughout the table.
The index is referred to as a unique index
.
A unique index is not a constraint.
Unique indexes cannot contain duplicate key values (or duplicate key value combinations, in the case of compound, or multi-column, or multi-segment) indexes.
Duplicated NULLs are permitted, in accordance with the SQL standard, in both single-segment and multi-segment indexes.
5.5.1.3. Partial Indexes
Specifying the WHERE clause in the index creation statement creates a partial index (also knows as filtered index).
A partial index contains only rows that match the search condition of the WHERE.
A partial index definition may include the UNIQUE clause.
In this case, every key in the index is required to be unique.
This allows enforcing uniqueness for a subset of table rows.
A partial index is usable only in the following cases:
The
WHEREclause of the statement includes exactly the same boolean expression as the one defined for the index;The search condition defined for the index contains ORed boolean expressions and one of them is explicitly included in the
WHEREclause of the statement;The search condition defined for the index specifies
IS NOT NULLand theWHEREclause of the statement includes an expression on the same field that is known to exclude NULLs.
5.5.1.4. Index Direction
All indexes in Firebird are uni-directional.
An index may be constructed from the lowest value to the highest (ascending order) or from the highest value to the lowest (descending order).
The keywords ASC[ENDING] and DESC[ENDING] are used to specify the direction of the index.
The default index order is ASC[ENDING].
It is valid to define both an ascending and a descending index on the same column or key set.
A descending index can be useful on a column that will be subjected to searches on the high values (newest
, maximum, etc.)
5.5.1.5. Computed (Expression) Indexes
In creating an index, you can use the COMPUTED BY clause to specify an expression instead of one or more columns.
Computed indexes are used in queries where the condition in a WHERE, ORDER BY or GROUP BY clause exactly matches the expression in the index definition.
The expression in a computed index may involve several columns in the table.
Expression indexes can also be used as a workaround for indexing computed columns: use the name of the computed column as the expression.
5.5.1.6. Limits on Indexes
Certain limits apply to indexes.
The maximum length of a key in an index is limited to a quarter of the page size
A compound index can have at most 16 columns
5.5.1.6.1. Maximum Indexes per Table
The number of indexes that can be accommodated for each table is limited. The actual maximum for a specific table depends on the page size and the number of columns in the indexes.
Page Size | Number of Indexes Depending on Column Count | ||
Single | 2-Column | 3-Column | |
4096 | 203 | 145 | 113 |
8192 | 408 | 291 | 227 |
16384 | 818 | 584 | 454 |
32768 | 1637 | 1169 | 909 |
5.5.1.6.2. Character Index Limits
The maximum indexed string length is 9 bytes less than the maximum key length. The maximum indexable string length depends on the page size, the character set, and the collation.
Page Size | Maximum Indexable String Length by Charset Type | |||
1 byte/char | 2 byte/char | 3 byte/char | 4 byte/char | |
4096 | 1015 | 507 | 338 | 253 |
8192 | 2039 | 1019 | 679 | 509 |
16384 | 4087 | 2043 | 1362 | 1021 |
32768 | 8183 | 4091 | 2727 | 2045 |
Depending on the collation, the maximum size can be further reduced as case-insensitive and accent-insensitive collations require more bytes per character in an index. See also Character Indexes in Chapter Data Types and Subtypes.
5.5.1.7. Parallelized Index Creation
Since Firebird 5.0, index creation can be parallelized.
Parallelization happens automatically if the current connection has two or more parallel workers — configured through ParallelWorkers in firebird.conf or isc_dpb_parallel_workers — and the server has parallel workers available.
5.5.1.8. Examples Using CREATE INDEX
Creating an index for the
UPDATER_IDcolumn in theSALARY_HISTORYtableCREATE INDEX IDX_UPDATERON SALARY_HISTORY (UPDATER_ID);Creating an index with keys sorted in the descending order for the
CHANGE_DATEcolumn in theSALARY_HISTORYtableCREATE DESCENDING INDEX IDX_CHANGEON SALARY_HISTORY (CHANGE_DATE);Creating a multi-segment index for the
ORDER_STATUS,PAIDcolumns in theSALEStableCREATE INDEX IDX_SALESTATON SALES (ORDER_STATUS, PAID);Creating an index that does not permit duplicate values for the
NAMEcolumn in theCOUNTRYtableCREATE UNIQUE INDEX UNQ_COUNTRY_NAMEON COUNTRY (NAME);Creating a computed index for the
PERSONStableCREATE INDEX IDX_NAME_UPPER ON PERSONSCOMPUTED BY (UPPER (NAME));An index like this can be used for a case-insensitive search:
SELECT *FROM PERSONSWHERE UPPER(NAME) STARTING WITH UPPER('Iv');Creating a partial index and using its condition:
CREATE INDEX IT1_COL ON T1 (COL) WHERE COL < 100;SELECT * FROM T1 WHERE COL < 100;-- PLAN (T1 INDEX (IT1_COL))Creating a partial index which excludes NULL
CREATE INDEX IT1_COL2 ON T1 (COL) WHERE COL IS NOT NULL;SELECT * FROM T1 WHERE COL > 100;PLAN (T1 INDEX IT1_COL2)Creating a partial index with ORed conditions
CREATE INDEX IT1_COL3 ON T1 (COL) WHERE COL = 1 OR COL = 2;SELECT * FROM T1 WHERE COL = 2;-- PLAN (T1 INDEX IT1_COL3)Using a partial index to enforce uniqueness for a subset of rows
create table OFFER (OFFER_ID bigint generated always as identity primary key,PRODUCT_ID bigint not null,ARCHIVED boolean default false not null,PRICE decimal(9,2) not null);create unique index IDX_OFFER_UNIQUE_PRODUCTon OFFER (PRODUCT_ID)where not ARCHIVED;insert into OFFER (PRODUCT_ID, ARCHIVED, PRICE) values (1, false, 18.95);insert into OFFER (PRODUCT_ID, ARCHIVED, PRICE) values (1, true, 17.95);insert into OFFER (PRODUCT_ID, ARCHIVED, PRICE) values (1, true, 16.95);-- Next fails due to second record for PRODUCT_ID=1 and ARCHIVED=false:insert into OFFER (PRODUCT_ID, ARCHIVED, PRICE) values (1, false, 19.95);-- Statement failed, SQLSTATE = 23000-- attempt to store duplicate value (visible to active transactions) in unique index "IDX_OFFER_UNIQUE_PRODUCT"-- -Problematic key value is ("PRODUCT_ID" = 1)
See alsoSection 5.5.2, “ALTER INDEX”, Section 5.5.3, “DROP INDEX”
5.5.2. ALTER INDEX
Activates or deactivates an index, and rebuilds an index
Available inDSQL, ESQL
Syntax
ALTER INDEX indexname {ACTIVE | INACTIVE}
ALTER INDEX Statement Parameter| Parameter | Description |
|---|---|
indexname | Index name |
The ALTER INDEX statement activates or deactivates an index.
There is no facility on this statement for altering any attributes of the index.
INACTIVEWith the
INACTIVEoption, the index is switched from the active to inactive state. The effect is similar to theDROP INDEXstatement except that the index definition remains in the database. Altering a constraint index to the inactive state is not permitted.An active index can be deactivated if there are no queries prepared using that index; otherwise, an
object in use
error is returned.Activating an inactive index is also safe. However, if there are active transactions modifying the table, the transaction containing the
ALTER INDEXstatement will fail if it has theNOWAITattribute. If the transaction is inWAITmode, it will wait for completion of concurrent transactions.On the other side of the coin, if our
ALTER INDEXsucceeds and starts to rebuild the index atCOMMIT, other transactions modifying that table will fail or wait, according to theirWAIT/NO WAITattributes. The situation is the same forCREATE INDEX.ⓘHow is it Useful?It might be useful to switch an index to the inactive state whilst inserting, updating or deleting a large batch of records in the table that owns the index.
ACTIVERebuilds the index (even if already active), and marks it as active.
ⓘHow is it Useful?Even if the index is active when
ALTER INDEX … ACTIVEis executed, the index will be rebuilt. Rebuilding indexes can be a useful piece of housekeeping to do, occasionally, on the indexes of a large table in a database that has frequent inserts, updates or deletes but is infrequently restored.
5.5.2.1. Who Can Alter an Index?
The ALTER INDEX statement can be executed by:
The owner of the table
Users with the
ALTER ANY TABLEprivilege
5.5.2.2. Use of ALTER INDEX on a Constraint Index
Altering the index of a PRIMARY KEY, FOREIGN KEY or UNIQUE constraint to INACTIVE is not permitted.
However, ALTER INDEX … ACTIVE works just as well with constraint indexes as it does with others, as an index rebuilding tool.
5.5.2.3. ALTER INDEX Examples
Deactivating the
IDX_UPDATERindexALTER INDEX IDX_UPDATER INACTIVE;Switching the
IDX_UPDATERindex back to the active state and rebuilding itALTER INDEX IDX_UPDATER ACTIVE;
See alsoSection 5.5.1, “CREATE INDEX”, Section 5.5.3, “DROP INDEX”, Section 5.5.4, “SET STATISTICS”
5.5.3. DROP INDEX
Drops an index
Available inDSQL, ESQL
Syntax
DROP INDEX indexname
DROP INDEX Statement Parameter| Parameter | Description |
|---|---|
indexname | Index name |
The DROP INDEX statement drops (deletes) the named index from the database.
A constraint index cannot be dropped using DROP INDEX.
Constraint indexes are dropped during the process of executing the command ALTER TABLE … DROP CONSTRAINT ….
5.5.3.1. Who Can Drop an Index?
The DROP INDEX statement can be executed by:
The owner of the table
Users with the
ALTER ANY TABLEprivilege
5.5.3.2. DROP INDEX Example
Dropping the IDX_UPDATER index
DROP INDEX IDX_UPDATER;
See alsoSection 5.5.1, “CREATE INDEX”, Section 5.5.2, “ALTER INDEX”
5.5.4. SET STATISTICS
Recalculates the selectivity of an index
Available inDSQL, ESQL
Syntax
SET STATISTICS INDEX indexname
SET STATISTICS Statement Parameter| Parameter | Description |
|---|---|
indexname | Index name |
The SET STATISTICS statement recalculates the selectivity of the specified index.
5.5.4.1. Who Can Update Index Statistics?
The SET STATISTICS statement can be executed by:
The owner of the table
Users with the
ALTER ANY TABLEprivilege
5.5.4.2. Index Selectivity
The selectivity of an index is the result of evaluating the number of rows that can be selected in a search on every index value. A unique index has the maximum selectivity because it is impossible to select more than one row for each value of an index key if it is used. Keeping the selectivity of an index up to date is important for the optimizer’s choices in seeking the most optimal query plan.
Index statistics in Firebird are not automatically recalculated in response to large batches of inserts, updates or deletions. It may be beneficial to recalculate the selectivity of an index after such operations because the selectivity tends to become outdated.
The statements CREATE INDEX and ALTER INDEX ACTIVE both store index statistics that correspond to the contents of the newly-[re]built index.
SET STATISTICS can be performed under concurrent load without risk of corruption.
However, under concurrent load, the newly calculated statistics could become outdated as soon as SET STATISTICS finishes.
5.5.4.3. Example Using SET STATISTICS
Recalculating the selectivity of the index IDX_UPDATER
SET STATISTICS INDEX IDX_UPDATER;
See alsoSection 5.5.1, “CREATE INDEX”, Section 5.5.2, “ALTER INDEX”