Правильные результаты?
Прежде всего: правильность. Вы хотите создать массив уникальных элементов? Ваш текущий запрос не делает этого. Функция uniq()
из модуля intarray обещает только:
удалить соседние дубликаты
Как указано в руководстве , вам потребуется:
SELECT l.d + r.d, uniq(sort(array_agg_mult(r.arr)))
FROM ...
Также дает вам отсортированные массивы - предполагая, что вы хотите, вы не уточнили.
Я вижу , у вас есть sort()
в вашей скрипке , так что это может быть просто опечатка в вашем вопросе.
Postgres 9,5
В любом случае, вам понравится новый Postgres 9.5 (в настоящее время бета). Это обеспечивает возможности array_agg_mult()
из коробки и намного быстрее:
Также были другие улучшения производительности для обработки массива.
запрос
Основная цель array_agg_mult()
состоит в агрегировании многомерных массивов, но в любом случае вы производите только одномерные массивы. Так что я бы хотя бы попробовал этот альтернативный запрос:
SELECT l.d + r.d AS d_sum, array_agg(DISTINCT elem) AS result_arr
FROM left2 l
JOIN right2 r USING (t1)
, unnest(r.arr) elem
GROUP BY 1
ORDER BY 1;
Который также отвечает на ваш вопрос:
Может ли агрегатная функция удалять дубликаты напрямую?
Да, может, с DISTINCT
. Но это не быстрее, чем uniq()
для целочисленных массивов, которые были оптимизированы для целочисленных массивов, и в то же время DISTINCT
являются общими для всех подходящих типов данных.
Не требует intarray
модуля. Однако результат не обязательно отсортирован. Postgres использует различные алгоритмы для DISTINCT
(IIRC), большие наборы обычно хэшируются, тогда результат не сортируется, если вы не добавите явное ORDER BY
. Если вам нужны отсортированные массивы, вы можете добавить ORDER BY
к агрегатной функции напрямую:
array_agg(DISTINCT elem ORDER BY elem)
Но это обычно медленнее, чем подача предварительно отсортированных данных array_agg()
(одна большая сортировка против множества мелких сортировок). Поэтому я бы отсортировал подзапрос, а затем агрегировал:
SELECT d_sum, uniq(array_agg(elem)) AS result_arr
FROM (
SELECT l.d + r.d AS d_sum, elem
FROM left2 l
JOIN right2 r USING (t1)
, unnest(r.arr) elem
ORDER BY 1, 2
) sub
GROUP BY 1
ORDER BY 1;
Это был самый быстрый вариант в моем беглом тесте на Postgres 9.4.
SQL Fiddle на основе того, который вы предоставили.
Индекс
Я не вижу большого потенциала для какого-либо индекса здесь. Единственный вариант будет:
CREATE INDEX ON right2 (t1, arr);
Это имеет смысл только в том случае, если вы получаете из этого только сканирование по индексу - что произойдет, если базовая таблица right2
будет значительно шире, чем только эти два столбца, и ваша установка будет соответствовать сканированию только по индексу. Подробности в Postgres Wiki.
right2.arr
быть NULL, как предполагает ваша демонстрационная схема? Вам нужны отсортированные массивы в результате?