intarray

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

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

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

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

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

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

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

ФункцияОписаниеПример(ы)
icount ( integer[] ) → integerВозвращает число элементов в массиве.icount('{1,2,3}'::integer[]) → 3
sort ( integer[], dir text ) → integer[]Сортирует массив в порядке возрастания или убывания. Направление задает параметр dir, значением которого должно быть 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[]Убирает соседние дубликаты.uniq(sort('{1,2,3,2,1}'::integer[])) → {1,2,3}
idx ( integer[], item integer ) → integerВозвращает индекс первого элемента массива, соответствующего item, или 0, если такого элемента нет.idx(array[11,22,33,22,11], 22) → 2
subarray ( integer[], start integer, len integer ) → integer[]Извлекает часть массива, начинающуюся с позиции start и содержащую len элементов.subarray('{1,2,3,2,1}'::integer[], 2, 3) → {2,3,2}
subarray ( integer[], start integer ) → integer[]Извлекает часть массива, начинающуюся с позиции start.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/. Андрей Октябрьский проделал отличную работу, добавив новые функции и операторы.