E.18. intarray

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

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

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

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

Реализованные в модуле intarray функции перечислены в Таблице E-8, а операторы — в Таблице E-9.

Таблица E-8. Функции intarray

ФункцияТип результатаОписаниеПримерРезультат
icount(int[])intчисло элементов в массивеicount('{1,2,3}'::int[])3
sort(int[], text dir)int[]сортирует массив — в dir должно задаваться asc (по возрастанию) или desc (по убыванию)sort('{1,2,3}'::int[], 'desc'){3,2,1}
sort(int[])int[]сортирует в порядке возрастанияsort(array[11,77,44]){11,44,77}
sort_asc(int[])int[]сортирует в порядке возрастания
sort_desc(int[])int[]сортирует в порядке убывания
uniq(int[])int[]удаляет дубликатыuniq(sort('{1,2,3,2,1}'::int[])){1,2,3}
idx(int[], int item)intиндекс первого элемента, равного item (0, если такого нет)idx(array[11,22,33,22,11], 22)2
subarray(int[], int start, int len)int[]часть массива, начинающаяся с позиции start и состоящая из len элементовsubarray('{1,2,3,2,1}'::int[], 2, 3){2,3,2}
subarray(int[], int start)int[]часть массива, начинающаяся с позиции startsubarray('{1,2,3,2,1}'::int[], 2){2,3,2,1}
intset(int)int[]создаёт массив с одним элементомintset(42){42}

Таблица E-9. Операторы intarray

ОператорВозвращаетОписание
int[] && int[]booleanпересекается с — true, если массивы имеют минимум один общий элемент
int[] @> int[]booleanвключает — true, если левый массив содержит правый массив
int[] <@ int[]booleanвключается в — true, если левый массив содержится в правом массиве
# int[]intчисло элементов в массиве
int[] # intintиндекс элемента (делает то же, что и функция idx)
int[] + intint[]вставляет элемент в массив (добавляет его в конец массива)
int[] + int[]int[]соединяет массивы (правый массив добавляется в конец левого)
int[] - intint[]удаляет из массива записи, равные правому аргументу
int[] - int[]int[]удаляет из левого массива элементы правого массива
int[] | intint[]объединение аргументов
int[] | int[]int[]объединение массивов
int[] & int[]int[]пересечение массивов
int[] @@ query_intbooleantrue, если массив удовлетворяет запросу (см. ниже)
query_int ~~ int[]booleantrue, если запросу удовлетворяет массив (коммутирующий оператор к @@)

(До версии PostgreSQL 8.2 операторы включения @> и <@ обозначались соответственно как @ и ~. Эти имена по-прежнему действуют, но считаются устаревшими и в конце концов будут упразднены. Заметьте, что старые имена произошли из соглашения, которому раньше следовали ключевые геометрические типы данных!)

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

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

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

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

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

Есть также нестандартный класс операторов GIN, gin__int_ops, поддерживающий те же операторы.

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

E.18.3. Пример

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

-- создать специализированный индекс
CREATE INDEX message_rdtree_idx ON message USING GIST (sections gist__int_ops);

-- вывести сообщения из секций 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;

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

В каталоге исходного кода contrib/intarray/bench содержится набор тестов производительности. Чтобы запустить эти тесты, выполните:

cd .../bench
createdb TEST
psql TEST < ../_int.sql
./create_test.pl | psql TEST
./bench.pl

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

E.18.5. Авторы

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