F.18. intagg — агрегатор и нумератор целых чисел #

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

F.18.1. Функции #

Агрегатор реализуется функцией int_array_aggregate(integer), которая выдаёт массив целых чисел, содержащий в точности те числа, что переданы ей. Это обёртка встроенной функции array_agg, которая делает то же самое для массива любого типа.

Нумератор реализуется функцией int_array_enum(integer[]), которая возвращает набор целых (setof integer). По сути его действие обратно действие агрегатора: получив массив целых, он разворачивает его в набор строк. Это оболочка функции unnest, которая делает то же самое для массива любого типа.

F.18.2. Примеры использования #

Во многих СУБД есть понятие таблицы соотношений «многие ко многим». Такая таблица обычно находится между двумя индексированными таблицами, например:

CREATE TABLE left_table  (id INT PRIMARY KEY, ...);
CREATE TABLE right_table (id INT PRIMARY KEY, ...);
CREATE TABLE many_to_many(id_left  INT REFERENCES left_table,
                          id_right INT REFERENCES right_table);

Как правило, она используется так:

SELECT right_table.*
FROM right_table JOIN many_to_many ON (right_table.id = many_to_many.id_right)
WHERE many_to_many.id_left = элемент;

Этот запрос вернёт все элементы из таблицы справа для записи в таблице слева. Это очень распространённая конструкция в SQL.

Однако этот подход может вызывать затруднения с очень большим количеством записей в таблице many_to_many. Часто такое соединение влечёт сканирование индекса и выборку каждой записи в таблице справа для конкретного элемента слева. Если у вас динамическая система, с этим ничего не поделать. Но если какое-то множество данных довольно статическое, вы можете создать сводную таблицу, применив агрегатор.

CREATE TABLE summary AS
  SELECT id_left, int_array_aggregate(id_right) AS rights
  FROM many_to_many
  GROUP BY id_left;

Эта команда создаст таблицу, содержащую одну строку для каждого элемента слева с массивом элементов справа. Она малополезна, пока не найден подходящий способ использования этого массива; именно для этого и нужен нумератор массива. Вы можете выполнить:

SELECT id_left, int_array_enum(rights) FROM summary WHERE id_left = элемент;

Приведённый выше запрос с вызовом int_array_enum выдаёт те же результаты, что и

SELECT id_left, id_right FROM many_to_many WHERE id_left = элемент;

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

На тестовом компьютере команда EXPLAIN показала, что стоимость запроса снизилась с 8488 до 329. Исходный запрос выполнял соединение с таблицей many_to_many и был заменён на:

SELECT id_right, count(id_right) FROM
  ( SELECT id_left, int_array_enum(rights) AS id_right
    FROM summary
    JOIN (SELECT id FROM left_table
          WHERE id = элемент) AS lefts
    ON (summary.id_left = lefts.id)
  ) AS list
  GROUP BY id_right
  ORDER BY count DESC;