CREATE INDEX
CREATE INDEX — создать индекс
Синтаксис
CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ [ IF NOT EXISTS ]имя
] ON [ ONLY ]имя_таблицы
[ USINGметод
] ( {имя_столбца
| (выражение
) } [ COLLATEправило_сортировки
] [класс_операторов
[ (параметр_класса_оп
=значение
[, ... ] ) ] ] [ ASC | DESC ] [ NULLS { FIRST | LAST } ] [, ...] ) [ INCLUDE (имя_столбца
[, ...] ) ] [ WITH (параметр_хранения
[=значение
] [, ... ] ) ] [ TABLESPACEтабл_пространство
] [ WHEREпредикат
]
Описание
CREATE INDEX
создаёт индексы по указанному столбцу(ам) заданного отношения, которым может быть таблица или материализованное представление. Индексы применяются в первую очередь для оптимизации производительности базы данных (хотя при неправильном использовании возможен и противоположный эффект).
Ключевое поле для индекса задаётся как имя столбца или выражение, заключённое в скобки. Если метод индекса поддерживает составные индексы, допускается указание нескольких полей.
Поле индекса может быть выражением, вычисляемым из значений одного или нескольких столбцов в строке таблицы. Это может быть полезно для получения быстрого доступа к данным по некоторому преобразованию исходных значений. Например, индекс, построенный по выражению upper(col)
, позволит использовать поиск по индексу в предложении WHERE upper(col) = 'JIM'
.
Postgres Pro предоставляет следующие методы индексов: B-дерево, хеш, GiST, SP-GiST, GIN и BRIN. Пользователи могут определить и собственные методы индексов, но это довольно сложная задача.
Если в команде присутствует предложение WHERE
, она создаёт частичный индекс. Такой индекс содержит записи только для части таблицы, обычно более полезной для индексации, чем остальная таблица. Например, если таблица содержит информацию об оплаченных и неоплаченных счетах, при этом последних сравнительно немного, но именно эта часть таблицы наиболее востребована, то увеличить быстродействие можно, создав индекс только по этой части. Ещё одно возможное применение WHERE
— добавив UNIQUE
, обеспечить уникальность в подмножестве таблицы. Подробнее это рассматривается в Разделе 11.8.
Выражение в предложении WHERE
может ссылаться только на столбцы нижележащей таблицы, но не обязательно ограничиваться теми, по которым строится индекс. В настоящее время в WHERE
также нельзя использовать подзапросы и агрегатные выражения. Это же ограничение распространяется и на выражения в полях индексов.
Все функции и операторы, используемые в определении индекса, должны быть «постоянными», то есть, их результаты должны зависеть только от аргументов, но не от внешних факторов (например, содержимого другой таблицы или текущего времени). Это ограничение обеспечивает определённость поведения индекса. Чтобы использовать в выражении индекса или в предложении WHERE
собственную функцию, не забудьте пометить её при создании как постоянную (IMMUTABLE).
Параметры
UNIQUE
Указывает, что система должна контролировать повторяющиеся значения в таблице при создании индекса (если в таблице уже есть данные) и при каждом добавлении данных. Попытки вставить или изменить данные, при которых будет нарушена уникальность индекса, будут завершаться ошибкой.
Когда уникальные индексы применяются к секционированным таблицам, действуют дополнительные ограничения; см. CREATE TABLE.
CONCURRENTLY
С этим указанием Postgres Pro построит индекс, не устанавливая никаких блокировок, которые бы предотвращали добавление, изменение или удаление записей в таблице, тогда как по умолчанию операция построения индекса блокирует запись (но не чтение) данных в таблице до своего завершения. С созданием индекса в этом режиме связан ряд особенностей, о которых следует знать, — см. Неблокирующее построение индексов.
Для временных таблиц
CREATE INDEX
всегда выполняется более простым, неблокирующим способом, так как они не могут использоваться никакими другими сеансами.IF NOT EXISTS
Не считать ошибкой, если индекс с таким именем уже существует. В этом случае будет выдано замечание. Заметьте, что нет никакой гарантии, что существующий индекс как-то соотносится с тем, который мог бы быть создан. Имя индекса является обязательным, когда указывается
IF NOT EXISTS
.INCLUDE
Необязательное предложение
INCLUDE
позволяет указать список столбцов, которые войдут в индекс как неключевые столбцы. Неключевой столбец не может использоваться в условии поиска для сканирования по индексу, и он не учитывается при анализе ограничения уникальности или исключения, устанавливаемого индексом. Однако при сканировании только индекса содержимое неключевых столбцов может быть получено без обращения к целевой таблице, так как оно находится непосредственно в элементе индекса. Таким образом, в результате добавления неключевых столбцов сканирование только индекса может использоваться в тех запросах, где иначе оно было бы неприменимо.Добавляя в индекс неключевые столбцы, особенно большого размера, есть смысл проявлять консерватизм. Если кортеж в индексе превышает максимально допустимый размер для данного типа индексов, вставить данные в таблицу не удастся. В неключевых столбцах дублируются данные из основной таблицы, что приводит к раздуванию индекса и может повлечь замедление запросов. Кроме того, если неключевой столбец содержится в индексе B-дерево, в таком индексе не будет работать исключение дубликатов.
Для столбцов, перечисленных в
INCLUDE
, не нужны соответствующие классы операторов; это предложение может содержать столбцы, для типов данных которых не определены классы операторов для заданного метода доступа.Выражения в неключевых столбцах не поддерживаются, так как их нельзя будет использовать при сканировании только индекса.
В настоящее время эту возможность поддерживают только методы доступа индексов B-дерево и GiST. В таких индексах значения столбцов, указанных в предложении
INCLUDE
, включаются в кортежи на уровне листьев, которые соответствуют кортежам кучи, но не включаются в элементы верхних уровней, используемые для навигации в дереве.имя
Имя создаваемого индекса. Указание схемы при этом не допускается; индекс всегда относится к той же схеме, что и родительская таблица. Если имя опущено, Postgres Pro формирует подходящее имя по имени родительской таблицы и именам индексируемых столбцов.
ONLY
Указывает, что индексы не должны рекурсивно создаваться в секциях секционированной таблицы. По умолчанию создание выполняется рекурсивно.
имя_таблицы
Имя индексируемой таблицы (возможно, дополненное схемой).
метод
Имя применяемого метода индекса. Возможные варианты:
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
(integer
)Фактор заполнения для индекса определяет в процентном отношении, насколько плотно метод индекса будет заполнять страницы индекса. Для B-деревьев концевые страницы заполняются до этого процента при начальном построении индекса и позже, при расширении индекса вправо (добавлении новых наибольших значений ключа). Если страницы впоследствии оказываются заполненными полностью, они будут разделены, что приводит к постепенному снижению эффективности индекса. Для B-деревьев по умолчанию используется фактор заполнения 90, но его можно поменять на любое целое значение от 10 до 100. Фактор заполнения, равный 100, полезен для статических таблиц и помогает уменьшить физический размер таблицы, но для интенсивно изменяемых таблиц лучше использовать меньшее значение, чтобы разделять страницы приходилось реже. С другими методами индекса фактор заполнения действует по-другому, но примерно в том же ключе; значение фактора заполнения по умолчанию для разных методов разное.
Индексы B-дерево дополнительно принимают этот параметр:
deduplicate_items
(boolean
)Этот параметр управляет механизмом исключения дубликатов, описанным в Подразделе 64.4.2. Он принимает логическое значение
ON
илиOFF
, соответственно включающее или отключающее это оптимизацию. (Другие возможные написанияON
иOFF
перечислены в Разделе 19.1.) Значение по умолчанию —ON
.Примечание
После выключения параметра
deduplicate_items
командойALTER INDEX
при добавлении в будущем новых элементов дубликаты исключаться не будут, но представление существующих кортежей не поменяется на стандартное.
Индексы GiST дополнительно принимают этот параметр:
buffering
(enum
)Определяет, будет ли при построении индекса использоваться буферизация, описанная в Подразделе 65.4.1. Со значением
OFF
она отключена, сON
— включена, а сAUTO
— отключена вначале, но может затем включиться на ходу, как только размер индекса достигнет значения effective_cache_size. По умолчанию подразумеваетсяAUTO
.
Индексы GIN принимают другие параметры:
fastupdate
(boolean
)Этот параметр управляет механизмом быстрого обновления, описанным в Подразделе 67.4.1. Он имеет логическое значение:
ON
включает быстрое обновление,OFF
отключает его. Значение по умолчанию —ON
.Примечание
Выключение
fastupdate
вALTER INDEX
предотвращает помещение добавляемых в дальнейшем строк в список записей, ожидающих индексации, но записи, добавленные в этот список ранее, в нём остаются. Чтобы очистить очередь операций, надо затем выполнитьVACUUM
для этой таблицы или вызвать функциюgin_clean_pending_list
.
gin_pending_list_limit
(integer
)Пользовательский параметр gin_pending_list_limit. Его значение задаётся в килобайтах.
Индексы BRIN принимают другие параметры:
pages_per_range
(integer
)Определяет, сколько блоков таблицы образуют зону блоков для каждой записи в индексе BRIN (за подробностями обратитесь к Разделу 68.1). Значение по умолчанию —
128
.autosummarize
(boolean
)Определяет, будет ли поставлен в очередь расчёт сводного значения для предыдущей зоны страниц, когда происходит добавление в следующую. За подробностями обратитесь к Подразделу 68.1.1. По умолчанию
off
.
Неблокирующее построение индексов
Создание индекса может мешать обычной работе с базой данных. Обычно Postgres Pro блокирует запись в индексируемую таблицу и выполняет всю операцию построения индекса за одно сканирование таблицы. Другие транзакции могут продолжать читать таблицу, но при попытке вставить, изменить или удалить строки в таблице они будут заблокированы до завершения построения индекса. Это может оказать нежелательное влияние на работу производственной базы данных. Индексация очень больших таблиц может занимать много часов, и даже для маленьких таблиц построение индекса может заблокировать записывающие процессы на время, неприемлемое для производственной системы.
Postgres Pro поддерживает построение индексов без блокировки записи. Этот метод выбирается указанием CONCURRENTLY
команды CREATE INDEX
. Когда он используется, Postgres Pro должен выполнить два сканирования таблицы, а кроме того, должен дождаться завершения всех существующих транзакций, которые потенциально могут модифицировать и использовать этот индекс. Таким образом, эта процедура требует проделать в сумме больше действий и выполняется значительно дольше, чем обычное построение индекса. Однако благодаря тому, что этот метод позволяет продолжать обычную работу с базой во время построения индекса, он оказывается полезным в производственной среде. Хотя разумеется, дополнительная нагрузка на процессор и подсистему ввода/вывода, создаваемая при построении индекса, может привести к замедлению других операций.
При неблокирующем построении индекса он попадает в системный каталог в одной транзакции, затем ещё два сканирования таблицы выполняются в двух других транзакциях. Перед каждым сканированием таблицы процедура построения индекса должна ждать завершения текущих транзакций, модифицировавших эту таблицу. После второго сканирования также необходимо дожидаться завершения всех транзакций, получивших снимок (см. Главу 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 INDEX CONCURRENTLY
.)
Ещё одна сложность, с которой можно столкнуться при неблокирующем построении уникального индекса, заключается в том, что ограничение уникальности уже влияет на другие транзакции, когда второе сканирование таблицы только начинается. Это значит, что нарушения ограничения могут проявляться в других запросах до того, как индекс становится доступным для использования и даже тогда, когда создать индекс в итоге не удаётся. Кроме того, если при втором сканировании происходит ошибка, «нерабочий» индекс оставляет в силе своё ограничение уникальности и дальше.
Метод неблокирующего построения поддерживает также индексы выражений и частичные индексы. Ошибки, произошедшие при вычислении этих выражений, могут привести к такому же поведению, как в вышеописанных случаях с нарушением ограничений уникальности.
Обычное построение индекса допускает одновременное построение других индексов для таблицы обычным методом, но неблокирующее построение для конкретной таблицы в один момент времени допускается только одно. Однако в любом случае никакие другие изменения схемы таблицы в это время не разрешаются. Другое отличие состоит в том, что в блоке транзакции может быть выполнена обычная команда CREATE INDEX
, но не CREATE INDEX CONCURRENTLY
.
Метод неблокирующего построения для индексов секционированных таблиц в настоящее время не поддерживается. Однако вы можете сократить время, на которое будет заблокирована секционированная таблица, построив в неблокирующем режиме индекс для каждой секции в отдельности и затем создав секционированный индекс в обычном режиме. В этом случае построение секционированного индекса будет заключаться только в изменении метаданных.
Примечания
Информацию о том, когда могут применяться, и когда не применяются индексы, и в каких конкретных ситуациях они могут быть полезны, можно найти в Главе 11.
В настоящее время составные индексы поддерживаются только методами B-дерево, GiST, GIN и BRIN. По умолчанию такой индекс может включать до 32 полей. (Этот предел можно изменить, пересобрав Postgres Pro.) Уникальные индексы поддерживает только B-дерево.
Для каждого столбца индекса можно задать класс операторов и дополнительные параметры класса. Данный класс определяет, какие операторы будут использоваться индексом для этого столбца. Например, индекс-B-дерево по четырёхбайтовым целым будет использовать класс int4_ops
; этот класс операторов включает функции сравнения для таких значений. На практике обычно достаточно использовать класс операторов по умолчанию для типа данных столбца. Существование классов операторов объясняется в первую очередь тем, что для некоторых типов данных можно предложить более одного осмысленного порядка сортировки. Например, может возникнуть желание отсортировать комплексные числа как по абсолютному значению, так и по вещественной части. Это можно сделать, определив два класса операторов для типа данных и выбрав подходящий класс при создании индекса. За дополнительными сведениями о классах операторов обратитесь к Разделу 11.10 и Разделу 40.16.
Когда команда CREATE INDEX
вызывается для секционированной таблицы, по умолчанию её действие распространятся рекурсивно на все секции, с тем чтобы в них оказались соответствующие индексы. Сначала каждая секция проверяется на наличие равнозначного индекса, и, если таковой находится, он присоединяется как индекс секции к создаваемому, который таким образом становится родительским индексом. Если нужного индекса не оказывается, новый индекс автоматически создаётся и присоединяется к основному; имя индекса для каждой секции выбирается так же, как и при выполнении этой команды без имени индекса. С указанием ONLY
рекурсия не производится и индекс помечается как нерабочий. (Команда ALTER INDEX ... ATTACH PARTITION
пометит его как рабочий, когда он будет представлен во всех секциях). Однако заметьте, что в любой секции, создаваемой в будущем командой CREATE TABLE ... PARTITION OF
, соответствующий индекс появится автоматически, вне зависимости от данного указания.
Для методов индекса, поддерживающих сканирование по порядку (в настоящее время это поддерживает только B-дерево), можно изменить порядок сортировки индекса, добавив необязательные предложения ASC
, DESC
, NULLS FIRST
или NULLS LAST
. Так как упорядоченный индекс можно сканировать вперёд или назад, обычно не имеет смысла создавать индекс по убыванию (DESC
) для одного столбца — этот порядок сортировки можно получить и с обычным индексом. Эти параметры имеют смысл при создании составных индексов так, что они будут соответствовать порядку сортировки, указанному в запросе со смешанным порядком, например SELECT ... ORDER BY x ASC, y DESC
. Параметры NULLS
полезны, когда требуется реализовать поведение «NULL внизу», изменив стандартное «NULL вверху», в запросах, зависящих от индексов, чтобы избежать дополнительной сортировки.
Система регулярно собирает статистику по всем столбцам таблицы. Новые индексы, построенные без применения выражений, могут эффективно использовать эту статистику сразу. Однако для создаваемых индексов по выражениям требуется выполнить ANALYZE
или подождать, пока фоновый процесс автоочистки не проанализирует таблицу и не посчитает статистику для этих индексов.
Для большинства методов индексов скорость создания индекса зависит от значения maintenance_work_mem. Чем больше это значение, тем меньше времени требуется для создания индекса (если только заданное значение не превышает объём действительно доступной памяти, что влечёт за собой использование подкачки).
Postgres Pro может строить индексы, задействуя несколько процессоров для ускорения обработки строк таблицы. Это называется параллельным построением индексов. Для методов индексов, поддерживающих построение в параллельном режиме (в настоящее время это только B-дерево), параметр maintenance_work_mem
задаёт максимальный объём памяти, который может быть выделен для одной операции построения индекса в целом, независимо от того, сколько рабочих процессов будет запущено. Целесообразность использования параллельных процессов и их оптимальное количество обычно автоматически определяется моделью стоимости.
Параллельное построение индексов может выиграть от увеличения maintenance_work_mem
там, где для аналогичного последовательного построения индекса выигрыша не будет или он будет минимальным. Заметьте, что значение maintenance_work_mem
может влиять на число запрашиваемых рабочих процессов, так как параллельным исполнителям должно быть выделено не менее 32MB
из общего бюджета maintenance_work_mem
. Кроме того, 32MB
должно остаться для ведущего процесса. Увеличение max_parallel_maintenance_workers позволит создать больше исполнителей, что приведёт к уменьшению времени создания индекса, если только создание индекса уже не упирается в скорость ввода/вывода. Разумеется, для этого должно быть достаточно процессорных ресурсов, которые иначе бы простаивали.
Если в ALTER TABLE задаётся значение parallel_workers
, именно оно определяет, сколько параллельных исполнителей будет запрашивать команда CREATE INDEX
для данной таблицы. При этом полностью игнорируется модель стоимости и maintenance_work_mem
не влияет на определение количества параллельных исполнителей. Если параметру parallel_workers
в ALTER TABLE
присваивается 0, параллельное построение индексов для этой таблицы полностью отключается.
Подсказка
После изменения параметра parallel_workers
в ходе оптимизации построения индексов имеет смысл сбросить его. Это предотвратит нежелательные изменения планов запросов, так как parallel_workers
влияет на все параллельные сканирования таблицы.
Хотя CREATE INDEX
с указанием CONCURRENTLY
поддерживает параллельное построение без особых ограничений, фактически в параллельном режиме выполняется только первое сканирование таблицы.
Для удаления индекса применяется DROP INDEX.
Как и любая длительная транзакция, операция CREATE INDEX
с таблицей может повлиять на возможность удаления кортежей параллельной операцией VACUUM
с какой-либо другой таблицей.
В предыдущих выпусках Postgres Pro также поддерживался метод индекса R-дерево. Сейчас он отсутствует, так как он не даёт значительных преимуществ по сравнению с GiST. Указание USING rtree
команда CREATE INDEX
будет интерпретировать как USING gist
, для упрощения перевода старых баз на GiST.
Примеры
Создание уникального индекса-B-дерева по столбцу title
в таблице films
:
CREATE UNIQUE INDEX title_idx ON films (title);
Создание уникального индекса-B-дерева по столбцу title
с неключевыми столбцами director
и rating
в таблице films
:
CREATE UNIQUE INDEX title_idx ON films (title) INCLUDE (director, rating);
Создание индекса B-дерево без исключения дубликатов:
CREATE INDEX title_idx ON films (title) WITH (deduplicate_items = off);
Создание индекса по выражению 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
является языковым расширением Postgres Pro. Средства обеспечения индексов в стандарте SQL не описаны.