intarray

Модуль intarray предоставляет ряд полезных функций и операторов для работы с массивами целых чисел без NULL. Также для некоторых из этих операторов он поддерживает поиск по индексу.

Все эти операции выдадут ошибку, если передаваемый массив содержит элементы NULL.

Многие из этих операций имеют смысл только с одномерными массивами. Хотя они примут на вход массивы и большей размерности, данные из них будут обрабатываться как из линейного массива в порядке хранения.

Этот модуль считается «доверенным», то есть его могут устанавливать обычные пользователи с правом CREATE в текущей базе данных.


Функции и операторы intarray

Предоставляемые модулем intarray функции перечислены в Таблице 9, а операторы — в Таблице 10.

Таблица 9. Функции intarray

Функция
Описание
Пример(ы)
icount ( integer[] ) → integer
Возвращает число элементов в массиве.
icount('{1,2,3}'::integer[]) → 3
sort ( integer[], напр text ) → integer[]
Сортирует массив в порядке возрастания или убывания. Направление задает параметр напр, значением которого должно быть asc (по возрастанию) или desc (по убыванию).
sort('{1,3,2}'::integer[], 'desc') → {3,2,1}
sort ( integer[] ) → integer[]
sort_asc ( integer[] ) → integer[]
Сортирует в порядке возрастания.
sort(array[11,77,44]) → {11,44,77}
sort_desc ( integer[] ) → integer[]
Сортирует в порядке убывания.
sort_desc(array[11,77,44]) → {77,44,11}
uniq ( integer[] ) → integer[]
Удаляет соседние дубликаты. Часто используется вместе с sort для удаления всех дубликатов
uniq('{1,2,2,3,1,1}'::integer[]) → {1,2,3,1}
uniq(sort('{1,2,3,2,1}'::integer[])) → {1,2,3}
idx ( integer[], элемент integer ) → integer
Возвращает индекс первого элемента массива, соответствующего заданному элементу, или 0, если такого элемента нет.
idx(array[11,22,33,22,11], 22) → 2
subarray ( integer[], начало integer, длина integer ) → integer[]
Извлекает часть массива, начинающуюся с позиции начало и длиной в заданное число элементов.
subarray('{1,2,3,2,1}'::integer[], 2, 3) → {2,3,2}
subarray ( integer[], начало integer ) → integer[]
Извлекает часть массива, начинающуюся с позиции начало.
subarray('{1,2,3,2,1}'::integer[], 2) → {2,3,2,1}
intset ( integer ) → integer[]
Создает массив с одним элементом.
intset(42) → {42}

Таблица 10. Операторы intarray

Оператор
Описание
integer[] && integer[] → boolean
Массивы пересекаются (имеют хотя бы один общий элемент)?
integer[] @> integer[] → boolean
Левый массив содержит правый?
integer[] <@ integer[] → boolean
Левый массив содержится в правом?
# integer[] → integer
Возвращает число элементов в массиве.
integer[] # integer → integer
Возвращает индекс первого элемента массива, соответствующего правому аргументу, или 0, если такого элемента нет. (Аналог функции idx.)
integer[] + integer → integer[]
Добавляет элемент в конец массива.
integer[] + integer[] → integer[]
Конкатенирует массивы.
integer[] - integer → integer[]
Убирает из массива записи, соответствующие правому аргументу.
integer[] - integer[] → integer[]
Убирает из левого массива элементы правого массива.
integer[] | integer → integer[]
Вычисляет объединение аргументов.
integer[] | integer[] → integer[]
Вычисляет объединение аргументов.
integer[] & integer[] → integer[]
Вычисляет пересечение аргументов.
integer[] @@ query_int → boolean
Массив удовлетворяет запросу? (см. ниже)
query_int ~~ integer[] → boolean
Массив удовлетворяет запросу? (коммутатор к @@)

Операторы &&, @> и <@ равнозначны встроенным операторам QHB с теми же именами, за исключением того, что они работают только с целочисленными массивами, не содержащими NULL, тогда как встроенные операторы работают с массивами любого типа. Из-за этого ограничения они в большинстве случаев работают быстрее встроенных операторов.

Операторы @@ и ~~ проверяют, удовлетворяет ли массив запросу, который выражается в виде значения специализированного типа данных query_int. Запрос состоит из целочисленных значений, сравниваемых с элементами массива, возможно, с применением операторов & (AND), | (OR) и ! (NOT). При необходимости можно использовать скобки. Например, запросу 1&(2\|3) соответствуют массивы, которые содержат 1 и также содержат 2 или 3.


Поддержка индексов

Модуль intarray поддерживает индексы для операторов &&, @> и @@ наряду с обычной проверкой равенства массивов.

Модуль предоставляет два параметризированных класса операторов индексов GiST: gist__int_ops (используемый по умолчанию) подходит для маленьких и средних по размеру наборов данных, тогда как gist__intbig_ops применяет сигнатуру большего размера и лучше подходит для индексации больших наборов данных (т. е. столбцов, содержащих много уникальных значений массива). В этой реализации используется структура данных RD-дерева со встроенным сжатием с потерями.

Класс gist__int_ops аппроксимирует набор целых чисел в виде массива целочисленных диапазонов. Его необязательный целочисленный параметр numranges определяет максимальное количество диапазонов в одном ключе индекса. Значение по умолчанию — 100. Допустимые значения лежат в пределах от 1 до 253. При увеличении размера массивов, составляющих ключ индекса GiST, поиск работает точнее (сканируется меньшая область индекса и меньше страниц кучи), но сам индекс становится больше.

Класс gist__intbig_ops аппроксимирует набор целых чисел в виде сигнатуры битовой карты. Его необязательный целочисленный параметр siglen определяет длину сигнатуры в байтах. Значение по умолчанию — 16 байт. Допустима длина сигнатуры в пределах от 1 до 2024 байт. При увеличении размера сигнатур поиск работает точнее (сканируется меньшая область индекса и меньше страниц кучи), но сам индекс становится больше.

Имеется также нестандартный класс операторов GIN, gin__int_ops, который поддерживает эти операторы наряду с <@.

Выбор между поиском по индексам GiST и GIN зависит от относительных характеристик производительности GiST и GIN, которые здесь не рассматриваются.


Пример

-- сообщение может находиться в одной или нескольких «секциях»
CREATE TABLE message (mid INT PRIMARY KEY, sections INT[], ...);

-- создать специализированный индекс с длиной сигнатуры 32 байта
CREATE INDEX message_rdtree_idx ON message USING GIST (sections gist__intbig_ops (siglen = 32));

-- выбрать сообщения в секции 1 ИЛИ 2 - оператор ПЕРЕСЕЧЕНИЯ
SELECT message.mid FROM message WHERE message.sections && '{1,2}';

-- выбрать сообщения в секциях 1 И 2 - оператор ВКЛЮЧЕНИЯ
SELECT message.mid FROM message WHERE message.sections @> '{1,2}';

-- то же самое, но с применением оператора ЗАПРОСА
SELECT message.mid FROM message WHERE message.sections @@ '1&2'::query_int;

Тестирование производительности

В каталоге исходного кода contrib/intarray/bench содержится набор тестов производительности, которые можно провести на установленном сервере QHB. (Для этого также нужно установить пакет DBD::Pg.) Для запуска тестов выполните:

cd .../contrib/intarray/bench
createdb TEST
psql -c "CREATE EXTENSION intarray" TEST
./create_test.pl | psql TEST
./bench.pl

Скрипт bench.pl имеет несколько параметров, которые отображаются, если запустить его без аргументов.


Авторы

Разработку осуществили Федор Сигаев (teodor@sigaev.ru) и Олег Бартунов (oleg@sai.msu.su). Дополнительную информацию см. на странице http://www.sai.msu.su/~megera/postgres/gist/. Андрей Октябрьский проделал отличную работу, добавив новые функции и операторы.