Предположим, у нас есть таблица с четырьмя столбцами (a,b,c,d)
одного типа данных.
Можно ли выбрать все отдельные значения в данных в столбцах и вернуть их в виде одного столбца, или мне нужно создать функцию для достижения этой цели?
postgresql
postgresql-performance
postgresql-9.4
distinct
Фабрицио Маззони
источник
источник
SELECT a FROM tablename UNION SELECT b FROM tablename UNION SELECT c FROM tablename UNION SELECT d FROM tablename ;
?UNION
Ответы:
Обновление: протестированы все 5 запросов в SQLfiddle с 100K строк (и 2 отдельных случая, один с несколькими (25) различными значениями и другой с партиями (около 25K значений).
Очень простой запрос будет использовать
UNION DISTINCT
.Я думаю, что было бы наиболее эффективно, если бы для каждого из четырех столбцовбыл отдельный индекс. Было бы эффективно использовать отдельный индекс для каждого из четырех столбцов, если бы в Postgres была реализована оптимизация Loose Index Scan , чего у него нет. Таким образом, этот запрос не будет эффективным, поскольку он требует 4 сканирования таблицы (и индекс не используется):Другой будет сначала,
UNION ALL
а затем использоватьDISTINCT
. Это также потребует 4 сканирования таблицы (и не использовать индексы). Неплохая эффективность, когда значений немного, и с большим количеством значений становится самым быстрым в моем (не обширном) тесте:Другие ответы предоставили больше опций с использованием функций массива или
LATERAL
синтаксиса. Джек query (187 ms, 261 ms
) имеет разумную производительность, но запрос AndriyM кажется более эффективным (125 ms, 155 ms
). Оба они выполняют одно последовательное сканирование таблицы и не используют какой-либо индекс.На самом деле результаты запросов Джека немного лучше, чем показано выше (если мы удалим их
order by
), и их можно улучшить, удалив 4 внутреннихdistinct
и оставив только внешний.Наконец, если - и только если - отдельные значения 4 столбцов относительно немного, вы можете использовать метод
WITH RECURSIVE
взлома / оптимизации, описанный на приведенной выше странице Loose Index Scan, и использовать все 4 индекса, с удивительно быстрым результатом! Протестировано с теми же строками 100K и примерно 25 различными значениями, распределенными по 4 столбцам (работает всего за 2 мс!), В то время как с 25K различными значениями это самое медленное с 368 мс:SQLfiddle
Подводя итог, можно сказать, что при небольшом количестве различных значений рекурсивный запрос является абсолютным победителем, в то время как с большим количеством значений мой 2-й, Джек (улучшенная версия ниже) и запросы AndriyM - лучшие исполнители.
Позднее добавление - вариант первого запроса, который, несмотря на дополнительные отчетливые операции, работает намного лучше, чем исходный первый, и лишь немного хуже второго:
и Джек улучшился:
источник
Вы можете использовать LATERAL, как в этом запросе :
Ключевое слово LATERAL позволяет правой стороне объединения ссылаться на объекты с левой стороны. В этом случае правая сторона - это конструктор VALUES, который строит подмножество из одного столбца из значений столбцов, которые вы хотите поместить в один столбец. Основной запрос просто ссылается на новый столбец, также применяя к нему DISTINCT.
источник
Чтобы было ясно, я бы использовал,
union
как подсказывает ypercube , но это также возможно с массивами:dbfiddle здесь
источник
самый короткий
Менее многословная версия идеи Андрея лишь немного длиннее, но более элегантна и быстрее.
Для многих различных / нескольких повторяющихся значений:
Самый быстрый
С индексом на каждый задействованный столбец!
Для несколько различных / много повторяющихся значений:
Это еще один вариант rCTE, похожий на уже опубликованный @ypercube , но
ORDER BY 1 LIMIT 1
вместоmin(a)
него я обычно использую его немного быстрее. Мне также не нужен дополнительный предикат для исключения значений NULL.И
LATERAL
вместо коррелированного подзапроса, потому что он чище (не обязательно быстрее).Подробное объяснение в моем кратком ответе на эту технику:
Я обновил SQL Fiddle в ypercube и добавил мой в список воспроизведения.
источник
EXPLAIN (ANALYZE, TIMING OFF)
чтобы проверить лучшую общую производительность? (Лучшее из 5, чтобы исключить эффекты кэширования.)VALUES ...
быстрееunnest(ARRAY[...])
.LATERAL
неявно для функций, возвращающих множество вFROM
списке.Вы можете, но, как я написал и проверил функцию, я чувствовал себя неправильно. Это трата ресурсов.
Просто, пожалуйста, используйте союз и выберите больше. Единственное преимущество (если оно есть), одно сканирование из основной таблицы.
В sql fiddle вам нужно изменить разделитель с $ на что-то другое, например /
источник