Spec-Zone.ru › SQLite

Частичные индексы

Содержание
1. Введение
2. Создание частичных индексов
2.1. Уникальные частичные индексы
3. Запросы с использованием частичных индексов
4. Поддерживаемые версии

1. Введение

Частичный индекс — это индекс, охватывающий подмножество строк таблицы.

В обычных индексах для каждой строки таблицы существует ровно одна запись в индексе. В частичных индексах записи в индексе есть только для некоторых строк таблицы. Например, частичный индекс может пропускать записи, для которых значение индексируемого столбца равно NULL. При разумном использовании частичные индексы могут привести к уменьшению размера файлов базы данных и улучшению производительности запросов и записи.

2. Создание частичных индексов

Создайте частичный индекс, добавив оператор WHERE в конец обычного оператора CREATE INDEX.

create-index-stmt:

CREATE UNIQUE INDEX IF NOT EXISTS имя_схемы . имя_индекса НА имя_таблицы ( индексированный_столбец ) , ГДЕ выражение

выражение:

литеральное значение связываемый параметр имя схемы . имя таблицы . имя столбца унарный оператор expr expr бинарный оператор expr имя функции ( аргументы функции ) фильтрующее предложение over-предложение ( expr ) , CAST ( expr AS имя типа
) выражение COLLATE collation-name выражение НЕ LIKE GLOB REGEXP MATCH выражение выражение ESCAPE выражение выражение ISNULL NOTNULL НЕ NULL выражение IS НЕ DISTINCT FROM выражение
expr НЕ МЕЖДУ expr И expr expr НЕ В ( выражение-выбора ) expr , имя_схемы . функция_таблицы ( expr ) имя_таблицы , НЕ СУЩЕСТВУЕТ ( выражение-выбора )
CASE expr WHEN expr THEN expr ELSE expr END raise-function

фильтр-оператор:

ФИЛЬТР ( ГДЕ выражение )

аргументы_функции:

DISTINCT expr , * ORDER BY ordering-term ,

ordering-term:

expr COLLATE collation-name DESC ASC NULLS FIRST NULLS LAST

literal-value:

CURRENT_TIMESTAMP numeric-literal string-literal blob-literal NULL TRUE FALSE CURRENT_TIME CURRENT_DATE

over-оператор:

ПЕРЕД имя-окна ( базовое-имя-окна РАЗДЕЛЁННЫЙ ПО выражение , ПОРЯДОК ПО определяемое-свойство , спецификация-рамки )

спецификация-рамки:

ГРУППЫ МЕЖДУ НЕОГРАНИЧЕННЫХ ПРЕДШЕСТВУЮЩИХ И НЕОГРАНИЧЕННЫХ СЛЕДУЮЩИХ ОБЛАСТЬ СТРОКИ НЕОГРАНИЧЕННЫХ ПРЕДШЕСТВУЮЩИХ expr ПРЕДШЕСТВУЮЩИХ ТЕКУЩЕЙ СТРОКИ expr ПРЕДШЕСТВУЮЩИХ ТЕКУЩЕЙ СТРОКИ expr СЛЕДУЮЩИХ expr ПРЕДШЕСТВУЮЩИХ ТЕКУЩЕЙ СТРОКИ expr СЛЕДУЮЩИХ
Исключить Текущий Строка Исключить Группа Исключить Связи Исключить Нет Другие

ordering-term:

выражение СОПОСТАВИТЬ имя_сопоставления УБЫВАЮЩИЙ ВОЗРАСТАЮЩИЙ ПУСТОЙ первый ПУСТОЙ последний

raise-функция:

ПОДНЯТЬ ( ОТКАТ , сообщение об ошибке ) ИГНОРИРОВАТЬ ПРЕКРАТИТЬ ПРОВАЛ

выражение select:

С РЕКУРСИВНЫЙ общий-выражение-с-таблицей , ВЫБЕРИ УНИКАЛЬНЫЕ столбец-результата , ВСЕ ИЗ таблицы-или-подзапроса объединение-условий , ГДЕ выражение ГРУППИРОВКА ПО выражение ПРИ выражение ,
ОКОНО имя_окна КАК определение_окна , ЗНАЧЕНИЯ ( выражение ) , , составной_оператор выбор_ядра ПОРЯДОК ПО ОГРАНИЧЕНИЕ выражение термин_упорядочивания , СМЕЩЕНИЕ выражение , выражение

общее_выражение_с_таблицей:

имя_таблицы ( имя_столбца ) AS НЕ МАТЕРИАЛИЗОВАННЫЙ ( выражение_select ) ,

сложный_оператор:

ОБЪЕДИНЕНИЕ ОБЪЕДИНЕНИЕ ПЕРЕСЕЧЕНИЕ ИСКЛЮЧЕНИЕ ВСЕ

оператор_соединения:

таблица-или-подзапрос оператор-соединения таблица-или-подзапрос ограничение-соединения

ограничение-соединения:

ИСПОЛЬЗУЯ ( имя-столбца ) , ПО выражение

оператор-соединения:

ЕСТЕСТВЕННЫЙ ЛЕВЫЙ ВНЕШНИЙ СОЕДИНЕНИЕ , ПРАВЫЙ ПОЛНЫЙ ВНУТРЕННИЙ КРОСС

условие-сортировки:

expr COLLATE collation-name DESC ASC NULLS FIRST NULLS LAST

столбец результата:

expr AS имя_псевдонима_столбца * имя_таблицы . *

таблица или подзапрос:

имя-схемы . название-таблицы КАК псевдоним-таблицы ИНДЕКСИРОВАН ПО имя-индекса НЕ ИНДЕКСИРОВАН имя-функции-таблицы ( выражение ) , КАК псевдоним-таблицы ( запрос-выбор ) ( таблица-или-подзапрос ) ,
join-clause

window-defn:

( base-window-name PARTITION BY expr , ORDER BY ordering-term , frame-spec )

frame-spec:

ГРУППЫ МЕЖДУ НЕОГРАНИЧЕННЫЕ ПРЕДШЕСТВУЮЩИЕ И НЕОГРАНИЧЕННЫЕ СЛЕДУЮЩИЕ ДИАПАЗОН СТРОКИ НЕОГРАНИЧЕННЫЕ ПРЕДШЕСТВУЮЩИЕ expr ПРЕДШЕСТВУЮЩИЕ ТЕКУЩИЙ СТРОКА expr ПРЕДШЕСТВУЮЩИЕ ТЕКУЩИЙ СТРОКА expr СЛЕДУЮЩИЕ expr ПРЕДШЕСТВУЮЩИЕ ТЕКУЩИЙ СТРОКА expr СЛЕДУЮЩИЕ
EXCLUDE CURRENT ROW EXCLUDE GROUP EXCLUDE TIES EXCLUDE NO OTHERS

type-name:

name ( signed-number , signed-number ) ( signed-number )

signed-number:

+ numeric-literal -

indexed-column:

название_столбца COLLATE имя_сортировки по_убыванию выражение по_возрастанию

Любой индекс, включающий в себя предложение WHERE в конце, считается частичным индексом. Индексы, которые опускают предложение WHERE (или индексы, созданные с помощью ограничений UNIQUE или PRIMARY KEY внутри операторов CREATE TABLE), являются обычными полными индексами.

Выражение после предложения WHERE может содержать операторы, литеральные значения и имена столбцов в индексируемой таблице. Предложение WHERE не может содержать подзапросы, ссылки на другие таблицы, недетерминированные функции или связанные параметры.

В индекс включаются только те строки таблицы, для которых предложение WHERE возвращает значение true. Если выражение предложения WHERE возвращает NULL или false для некоторых строк таблицы, эти строки пропускаются в индексе.

Столбцы, упомянутые в предложении WHERE частичного индекса, могут быть любыми столбцами в таблице, а не только теми, которые случайно индексируются. Однако очень часто выражение предложения WHERE частичного индекса представляет собой простое выражение над индексируемым столбцом. Вот типичный пример:

CREATE INDEX po_parent ON purchaseorder(parent_po) WHERE parent_po IS NOT NULL;

В приведенном примере, если большинство заказов на покупку не имеют родительского заказа на покупку, то большинство значений parent_po будут NULL. Это означает, что только небольшой подмножество строк в таблице purchaseorder будет индексировано. Следовательно, индекс займет гораздо меньше места. И изменения в исходной таблице purchaseorder будут выполняться быстрее, так как индекс po_parent нужно обновлять только для тех исключительных строк, где parent_po не NULL. Но индекс по-прежнему полезен для запросов. В частности, если нужно узнать всех «потомков» конкретного заказа на покупку "?1", запрос будет следующим:

SELECT po_num FROM purchaseorder WHERE parent_po=?1;

Вышеприведенный запрос будет использовать индекс po_parent для поиска ответа, так как индекс po_parent содержит записи для всех строк, представляющих интерес. Обратите внимание, что поскольку po_parent меньше, чем полный индекс, запрос, вероятно, будет выполняться быстрее.

2.1. Уникальные частичные индексы

Определение частичного индекса может включать ключевое слово UNIQUE. Если это так, SQLite требует, чтобы каждая запись в индексе была уникальной. Это предоставляет механизм для обеспечения уникальности среди некоторого подмножества строк в таблице.

Например, предположим, что у вас есть база данных членов большой организации, где каждый человек назначен на определённую «команду». Каждая команда имеет «лидера», который также является членом этой команды. Таблица может выглядеть примерно так:

CREATE TABLE person(
  person_id       INTEGER PRIMARY KEY,
  team_id         INTEGER REFERENCES team,
  is_team_leader  BOOLEAN,
  -- other fields elided
);

Поле team_id не может быть уникальным, потому что обычно несколько человек находятся в одной команде. Нельзя сделать комбинацию team_id и is_team_leader уникальной, так как обычно несколько нелидеров находятся в каждой команде. Решением для обеспечения одного лидера на команду является создание уникального индекса на team_id, но ограниченного теми записями, для которых is_team_leader имеет значение true:

CREATE UNIQUE INDEX team_leader ON person(team_id) WHERE is_team_leader;

Случайно, тот же самый индекс полезен для поиска лидера команды по определенной команде:

SELECT person_id FROM person WHERE is_team_leader AND team_id=?1;

3. Запросы, использующие частичные индексы

Пусть X — выражение в предложении WHERE частичного индекса, а W — предложение WHERE запроса, использующего индексируемую таблицу. Тогда запрос разрешено использовать частичный индекс, если W⇒X, где оператор ⇒ (обычно читается как «имплицирует») — логический оператор, эквивалентный «X или не W». Следовательно, определение того, может ли частичный индекс использоваться в конкретном запросе, сводится к доказательству теоремы в логике первого порядка.

В SQLite нет сложного доказателя теорем для определения W⇒X. Вместо этого SQLite использует два простых правила для нахождения общих случаев, когда W⇒X верно, и предполагает, что все остальные случаи ложны. Правила, используемые SQLite, следующие:

  1. Если W — соединённые оператором AND термины, а X — соединённые оператором OR термины, и если любой термин из W появляется как термин из X, то частичный индекс пригоден для использования.

    Например, пусть индекс —

    CREATE INDEX ex1 ON tab1(a,b) WHERE a=5 OR b=6;
    

    И пусть запрос —

    SELECT * FROM tab1 WHERE b=6 AND a=7; -- uses partial index
    

    Тогда индекс пригоден для использования в запросе, потому что термин «b=6» появляется как в определении индекса, так и в запросе. Помните: термины в индексе должны быть соединены оператором OR, а термины в запросе — оператором AND.

    Термины в W и X должны точно совпадать. SQLite не производит алгебраических преобразований, чтобы они выглядели одинаково. Термин «b=6» не соответствует «b=3+3» или «b-6=0» или «b BETWEEN 6 AND 6». «b=6» будет соответствовать «6=b», если «b=6» находится в индексе, а «6=b» — в запросе. Если термин вида «6=b» появляется в индексе, он никогда не будет соответствовать ничему.

  2. Если термин в X имеет вид «z IS NOT NULL», и если термин в W — оператор сравнения на «z», отличный от «IS», то эти термины совпадают.

    Пример: пусть индекс —

    CREATE INDEX ex2 ON tab2(b,c) WHERE c IS NOT NULL;
    

    Тогда любой запрос, использующий операторы =, <, >, <=, >=, <>, IN, LIKE или GLOB на столбце «c», будет пригоден для использования с частичным индексом, потому что эти операторы сравнения истинны только если «c» не равно NULL. Так, следующий запрос может использовать частичный индекс:

    SELECT * FROM tab2 WHERE b=456 AND c<>0;  -- uses partial index
    

    Но следующий запрос не может использовать частичный индекс:

    SELECT * FROM tab2 WHERE b=456;  -- cannot use partial index
    

    Последний запрос не может использовать частичный индекс, потому что могут быть строки в таблице с b=456 и где c равно NULL. Но эти строки не будут в частичном индексе.

Эти два правила описывают работу планировщика запросов в SQLite на момент написания (2013-08-01). И эти правила всегда будут соблюдаться. Однако будущие версии SQLite могут включить лучший доказатель теорем, который сможет найти другие случаи, когда W⇒X истинно, и таким образом может обнаружить больше случаев, когда частичные индексы полезны.

4. Поддерживаемые версии

Частичные индексы поддерживаются в SQLite начиная с версии 3.8.0 (2013-08-26).

Файлы баз данных, содержащие частичные индексы, не могут быть прочитаны или записаны версиями SQLite, предшествующими 3.8.0. Однако файл базы данных, созданный SQLite 3.8.0, всё ещё может быть прочитан и записан предыдущими версиями, если в его схеме нет частичных индексов. Базу данных, которая не может быть прочитана старыми версиями SQLite, можно сделать читаемой, просто выполнив DROP INDEX на частичных индексах.

SQLite is in the Public Domain.
https://sqlite.org/partialindex.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API