CREATE INDEX
CREATE INDEX — создать индекс
Синтаксис
CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ]имя
] ONимя_таблицы
[ USINGметод
] ( {имя_столбца
| (выражение
) } [ COLLATEправило_сортировки
] [класс_операторов
] [ ASC | DESC ] [ NULLS { FIRST | LAST } ] [, ...] ) [ WITH (параметр_хранения
[=значение
] [, ... ] ) ] [ TABLESPACEтабл_пространство
] [ WHEREпредикат
]
Описание
CREATE INDEX
создаёт индексы по указанному столбцу(ам) заданного отношения, которым может быть таблица или материализованное представление. Индексы применяются в первую очередь для оптимизации производительности базы данных (хотя при неправильном использовании возможен и противоположный эффект).
Ключевое поле для индекса задаётся как имя столбца или выражение, заключённое в скобки. Если метод индекса поддерживает составные индексы, допускается указание нескольких полей.
Поле индекса может быть выражением, вычисляемым из значений одного или нескольких столбцов в строке таблицы. Это может быть полезно для получения быстрого доступа к данным по некоторому преобразованию исходных значений. Например, индекс, построенный по выражению upper(col)
, позволит использовать поиск по индексу в предложении WHERE upper(col) = 'JIM'
.
PostgreSQL предоставляет следующие методы индексов: B-дерево, хеш, GiST, SP-GiST, GIN и BRIN. Пользователи могут определить и собственные методы индексов, но это довольно сложная задача.
Если в команде присутствует предложение WHERE
, она создаёт частичный индекс. Такой индекс содержит записи только для части таблицы, обычно более полезной для индексации, чем остальная таблица. Например, если таблица содержит информацию об оплаченных и неоплаченных счетах, при этом последних сравнительно немного, но именно эта часть таблицы наиболее востребована, то увеличить быстродействие можно, создав индекс только по этой части. Ещё одно возможное применение WHERE
— добавив UNIQUE
, обеспечить уникальность в подмножестве таблицы. Подробнее это рассматривается в Разделе 11.8.
Выражение в предложении WHERE
может ссылаться только на столбцы нижележащей таблицы, но не обязательно ограничиваться теми, по которым строится индекс. В настоящее время в WHERE
также нельзя использовать подзапросы и агрегатные выражения. Это же ограничение распространяется и на выражения в полях индексов.
Все функции и операторы, используемые в определении индекса, должны быть «постоянными», то есть, их результаты должны зависеть только от аргументов, но не от внешних факторов (например, содержимого другой таблицы или текущего времени). Это ограничение обеспечивает определённость поведения индекса. Чтобы использовать в выражении индекса или в предложении WHERE
собственную функцию, не забудьте пометить её при создании как постоянную (IMMUTABLE).
Параметры
UNIQUE
Указывает, что система должна контролировать повторяющиеся значения в таблице при создании индекса (если в таблице уже есть данные) и при каждом добавлении данных. Попытки вставить или изменить данные, при которых будет нарушена уникальность индекса, будут завершаться ошибкой.
CONCURRENTLY
С этим указанием PostgreSQL построит индекс, не устанавливая никаких блокировок, которые бы предотвращали добавление, изменение или удаление записей в таблице, тогда как по умолчанию операция построения индекса блокирует запись (но не чтение) данных в таблице до своего завершения. С созданием индекса в этом режиме связан ряд особенностей, о которых следует знать, — см. Неблокирующее построение индексов.
Для временных таблиц
CREATE INDEX
всегда выполняется более простым, неблокирующим способом, так как они не могут использоваться никакими другими сеансами.IF NOT EXISTS
Не считать ошибкой, если индекс с таким именем уже существует. В этом случае будет выдано замечание. Заметьте, что нет никакой гарантии, что существующий индекс как-то соотносится с тем, который мог бы быть создан. Имя индекса является обязательным, когда указывается
IF NOT EXISTS
.имя
Имя создаваемого индекса. Указание схемы при этом не допускается; индекс всегда относится к той же схеме, что и родительская таблица. Если имя опущено, PostgreSQL формирует подходящее имя по имени родительской таблицы и именам индексируемых столбцов.
имя_таблицы
Имя индексируемой таблицы (возможно, дополненное схемой).
метод
Имя применяемого метода индекса. Возможные варианты:
btree
,hash
,gist
,spgist
,gin
,brin
или установленный пользователем метод, например bloom. По умолчанию подразумевается методbtree
.имя_столбца
Имя столбца таблицы.
выражение
Выражение с одним или несколькими столбцами таблицы. Обычно выражение должно записываться в скобках, как показано в синтаксисе команды. Однако скобки можно опустить, если выражение записано в виде вызова функции.
правило_сортировки
Имя правила сортировки, применяемого для индекса. По умолчанию используется правило сортировки, заданное для индексируемого столбца, либо полученное для результата выражения индекса. Индексы с нестандартными правилами сортировки могут быть полезны для запросов, включающих выражения с такими правилами.
класс_операторов
Имя класса операторов. Подробнее об этом ниже.
ASC
Указывает порядок сортировки по возрастанию (подразумевается по умолчанию).
DESC
Указывает порядок сортировки по убыванию.
NULLS FIRST
Указывает, что значения NULL после сортировки оказываются перед остальными. Это поведение по умолчанию с порядком сортировки
DESC
.NULLS LAST
Указывает, что значения NULL после сортировки оказываются после остальных. Это поведение по умолчанию с порядком сортировки
ASC
.параметр_хранения
Имя специфичного для индекса параметра хранения. За подробностями обратитесь к Параметры хранения индекса.
табл_пространство
Табличное пространство, в котором будет создан индекс. Если не определено, выбирается default_tablespace, либо temp_tablespaces, при создании индекса временной таблицы.
предикат
Выражение ограничения для частичного индекса.
Параметры хранения индекса
Необязательное предложение WITH
определяет параметры хранения для индекса. У каждого метода индекса есть свой набор допустимых параметров хранения. Следующий параметр принимают методы B-дерево, хеш, GiST и SP-GiST:
fillfactor
Фактор заполнения для индекса определяет в процентном отношении, насколько плотно метод индекса будет заполнять страницы индекса. Для B-деревьев концевые страницы заполняются до этого процента при начальном построении индекса и позже, при расширении индекса вправо (добавлении новых наибольших значений ключа). Если страницы впоследствии оказываются заполненными полностью, они будут разделены, что приводит к постепенному снижению эффективности индекса. Для B-деревьев по умолчанию используется фактор заполнения 90, но его можно поменять на любое целое значение от 10 до 100. Фактор заполнения, равный 100, полезен для статических таблиц и помогает уменьшить физический размер таблицы, но для интенсивно изменяемых таблиц лучше использовать меньшее значение, чтобы разделять страницы приходилось реже. С другими методами индекса фактор заполнения действует по-другому, но примерно в том же ключе; значение фактора заполнения по умолчанию для разных методов разное.
Индексы GiST дополнительно принимают этот параметр:
buffering
Определяет, будет ли при построении индекса использоваться буферизация, описанная в Подразделе 62.4.1. Со значением
OFF
она отключена, сON
— включена, а сAUTO
— отключена вначале, но может затем включиться на ходу, как только размер индекса достигнет значения effective_cache_size. По умолчанию подразумеваетсяAUTO
.
Индексы GIN принимают другие параметры:
fastupdate
Этот параметр управляет механизмом быстрого обновления, описанным в Подразделе 64.4.1. Он имеет логическое значение:
ON
включает быстрое обновление,OFF
отключает его. (Другие возможные написанияON
иOFF
перечислены в Разделе 19.1.) Значение по умолчанию —ON
.Примечание
Выключение
fastupdate
вALTER INDEX
предотвращает помещение добавляемых в дальнейшем строк в список записей, ожидающих индексации, но записи, добавленные в этот список ранее, в нём остаются. Чтобы очистить очередь операций, надо затем выполнитьVACUUM
для этой таблицы или вызвать функциюgin_clean_pending_list
.
gin_pending_list_limit
Пользовательский параметр gin_pending_list_limit. Его значение задаётся в килобайтах.
Индексы BRIN принимают другие параметры:
pages_per_range
Определяет, сколько блоков таблицы образуют зону блоков для каждой записи в индексе BRIN (за подробностями обратитесь к Разделу 65.1). Значение по умолчанию —
128
.autosummarize
Определяет, будет ли производиться расчёт сводного значения для предыдущей зоны страниц, когда происходит добавление в следующую.
Неблокирующее построение индексов
Создание индекса может мешать обычной работе с базой данных. Обычно PostgreSQL блокирует запись в индексируемую таблицу и выполняет всю операцию построения индекса за одно сканирование таблицы. Другие транзакции могут продолжать читать таблицу, но при попытке вставить, изменить или удалить строки в таблице они будут заблокированы до завершения построения индекса. Это может оказать нежелательное влияние на работу производственной базы данных. Индексация очень больших таблиц может занимать много часов, и даже для маленьких таблиц построение индекса может заблокировать записывающие процессы на время, неприемлемое для производственной системы.
PostgreSQL поддерживает построение индексов без блокировки записи. Этот метод выбирается указанием CONCURRENTLY
команды CREATE INDEX
. Когда он используется, PostgreSQL должен выполнить два сканирования таблицы, а кроме того, должен дождаться завершения всех существующих транзакций, которые потенциально могут модифицировать и использовать этот индекс. Таким образом, эта процедура требует проделать в сумме больше действий и выполняется значительно дольше, чем обычное построение индекса. Однако благодаря тому, что этот метод позволяет продолжать обычную работу с базой во время построения индекса, он оказывается полезным в производственной среде. Хотя разумеется, дополнительная нагрузка на процессор и подсистему ввода/вывода, создаваемая при построении индекса, может привести к замедлению других операций.
При неблокирующем построении индекса он попадает в системный каталог в одной транзакции, затем ещё два сканирования таблицы выполняются в двух других транзакциях. Перед каждым сканированием таблицы процедура построения индекса должна ждать завершения текущих транзакций, модифицировавших эту таблицу. После второго сканирования также необходимо дожидаться завершения всех транзакций, получивших снимок (см. Главу 13) перед вторым сканированием, включая транзакции, задействованные на любом этапе неблокирующего построения индексов для других таблиц. Наконец индекс может быть помечен как готовый к использованию, после чего команда CREATE INDEX
завершается. Однако даже тогда индекс может быть не готов немедленно к применению в запросах: в худшем случае он не будет использоваться, пока существуют транзакции, начатые до начала построения индекса.
Если при сканировании таблицы возникает проблема, например взаимоблокировка или нарушение уникальности в уникальном индексе, команда CREATE INDEX
завершится ошибкой, но оставит после себя «нерабочий» индекс. Этот индекс будет игнорироваться при чтении данных, так как он может быть неполным; однако с ним могут быть связаны дополнительные операции при изменениях. В psql встроенная команда \d
помечает такой индекс как INVALID
:
postgres=# \d tab Table "public.tab" Column | Type | Collation | Nullable | Default --------+---------+-----------+----------+--------- col | integer | | | Indexes: "idx" btree (col) INVALID
Рекомендуемый в таких случаях способ исправления ситуации — удалить индекс и затем попытаться снова выполнить CREATE INDEX CONCURRENTLY
. (Кроме того, можно перестроить его с помощью команды REINDEX
. Но так как REINDEX
не поддерживает неблокирующий режим, вряд ли этот вариант будет желательным.)
Ещё одна сложность, с которой можно столкнуться при неблокирующем построении уникального индекса, заключается в том, что ограничение уникальности уже влияет на другие транзакции, когда второе сканирование таблицы только начинается. Это значит, что нарушения ограничения могут проявляться в других запросах до того, как индекс становится доступным для использования и даже тогда, когда создать индекс в итоге не удаётся. Кроме того, если при втором сканировании происходит ошибка, «нерабочий» индекс оставляет в силе своё ограничение уникальности и дальше.
Метод неблокирующего построения поддерживает также индексы выражений и частичные индексы. Ошибки, произошедшие при вычислении этих выражений, могут привести к такому же поведению, как в вышеописанных случаях с нарушением ограничений уникальности.
Обычное построение индекса допускает параллельное построение других индексов для таблицы обычным методом, но неблокирующее построение для конкретной таблицы в один момент времени допускается только одно. Однако в любом случае никакие другие изменения схемы таблицы в это время не разрешаются. Другое отличие состоит в том, что в блоке транзакции может быть выполнена обычная команда CREATE INDEX
, но не CREATE INDEX CONCURRENTLY
.
Замечания
Информацию о том, когда могут применяться, и когда не применяются индексы, и в каких конкретных ситуациях они могут быть полезны, можно найти в Главе 11.
В настоящее время составные индексы поддерживаются только методами B-дерево, GiST, GIN и BRIN. По умолчанию такой индекс может включать до 32 полей. (Этот предел можно изменить, пересобрав PostgreSQL.) Уникальные индексы поддерживает только B-дерево.
Для каждого столбца индекса можно задать класс операторов. Этот класс определяет, какие операторы будут использоваться индексом для этого столбца. Например, индекс-B-дерево по четырёхбайтовым целым будет использовать класс int4_ops
; этот класс операторов включает функции сравнения для таких значений. На практике обычно достаточно использовать класс операторов по умолчанию для типа данных столбца. Существование классов операторов объясняется в первую очередь тем, что для некоторых типов данных можно предложить более одного осмысленного порядка сортировки. Например, может возникнуть желание отсортировать комплексные числа как по абсолютному значению, так и по вещественной части. Это можно сделать, определив два класса операторов для типа данных и выбрав подходящий класс при создании индекса. За дополнительными сведениями о классах операторов обратитесь к Разделу 11.9 и Разделу 37.14.
Для методов индекса, поддерживающих сканирование по порядку (в настоящее время это поддерживает только B-дерево), можно изменить порядок сортировки индекса, добавив необязательные предложения ASC
, DESC
, NULLS FIRST
или NULLS LAST
. Так как упорядоченный индекс можно сканировать вперёд или назад, обычно не имеет смысла создавать индекс по убыванию (DESC
) для одного столбца — этот порядок сортировки можно получить и с обычным индексом. Эти параметры имеют смысл при создании составных индексов так, что они будут соответствовать порядку сортировки, указанному в запросе со смешанным порядком, например SELECT ... ORDER BY x ASC, y DESC
. Параметры NULLS
полезны, когда требуется реализовать поведение «NULL внизу», изменив стандартное «NULL вверху», в запросах, зависящих от индексов, чтобы избежать дополнительной сортировки.
Система регулярно собирает статистику по всем столбцам таблицы. Новые индексы, построенные без применения выражений, могут эффективно использовать эту статистику сразу. Однако для создаваемых индексов по выражениям требуется выполнить ANALYZE
или подождать, пока фоновый процесс автоочистки не проанализирует таблицу и не посчитает статистику для этих индексов.
Для большинства методов индексов скорость создания индекса зависит от значения maintenance_work_mem. Чем больше это значение, тем меньше времени требуется для создания индекса (если только заданное значение не превышает объём действительно доступной памяти, что влечёт за собой использование подкачки).
Для удаления индекса применяется DROP INDEX.
В предыдущих выпусках PostgreSQL также поддерживался метод индекса R-дерево. Сейчас он отсутствует, так как он не даёт значительных преимуществ по сравнению с GiST. Указание USING rtree
команда CREATE INDEX
будет интерпретировать как USING gist
, для упрощения перевода старых баз на GiST.
Примеры
Создание индекса-B-дерева по столбцу title
в таблице films
:
CREATE UNIQUE INDEX title_idx ON films (title);
Создание индекса по выражению lower(title)
, позволяющего эффективно выполнять регистронезависимый поиск:
CREATE INDEX ON films ((lower(title)));
(В этом примере мы решили опустить имя индекса, чтобы имя выбрала система, например films_lower_idx
.)
Создание индекса с нестандартным правилом сортировки:
CREATE INDEX title_idx_german ON films (title COLLATE "de_DE");
Создание индекса с нестандартным порядком значений NULL:
CREATE INDEX title_idx_nulls_low ON films (title NULLS FIRST);
Создание индекса с нестандартным фактором заполнения:
CREATE UNIQUE INDEX title_idx ON films (title) WITH (fillfactor = 70);
Создание индекса GIN с отключённым механизмом быстрого обновления:
CREATE INDEX gin_idx ON documents_table USING GIN (locations) WITH (fastupdate = off);
Создание индекса по столбцу code
в таблице films
и размещение его в табличном пространстве indexspace
:
CREATE INDEX code_idx ON films (code) TABLESPACE indexspace;
Создание индекса GiST по координатам точек, позволяющего эффективно использовать операторы box с результатом функции преобразования:
CREATE INDEX pointloc ON points USING gist (box(location,location)); SELECT * FROM points WHERE box(location,location) && '(0,0),(1,1)'::box;
Создание индекса без блокировки записи в таблицу:
CREATE INDEX CONCURRENTLY sales_quantity_index ON sales_table (quantity);
Совместимость
CREATE INDEX
является языковым расширением PostgreSQL. Средства обеспечения индексов в стандарте SQL не описаны.