Actualización: Probé las 5 consultas en SQLfiddle con 100K filas (y 2 casos separados, uno con pocos (25) valores distintos y otro con lotes (alrededor de 25K valores).
Una consulta muy simple sería usar UNION DISTINCT
. Creo que sería más eficiente si hubiera un índice separado en cada una de las cuatro columnas. Sería eficiente con un índice separado en cada una de las cuatro columnas, si Postgres hubiera implementado la optimización Loose Index Scan , que no lo ha hecho. Por lo tanto, esta consulta no será eficiente ya que requiere 4 escaneos de la tabla (y no se utiliza ningún índice):
-- Query 1. (334 ms, 368ms)
SELECT a AS abcd FROM tablename
UNION -- means UNION DISTINCT
SELECT b FROM tablename
UNION
SELECT c FROM tablename
UNION
SELECT d FROM tablename ;
Otro sería primero UNION ALL
y luego usar DISTINCT
. Esto también requerirá 4 escaneos de tabla (y sin uso de índices). No es una mala eficiencia cuando los valores son pocos, y con más valores se convierte en el más rápido en mi (no extensa) prueba:
-- Query 2. (87 ms, 117 ms)
SELECT DISTINCT a AS abcd
FROM
( SELECT a FROM tablename
UNION ALL
SELECT b FROM tablename
UNION ALL
SELECT c FROM tablename
UNION ALL
SELECT d FROM tablename
) AS x ;
Las otras respuestas han proporcionado más opciones utilizando funciones de matriz o la LATERAL
sintaxis. La consulta de Jack ( 187 ms, 261 ms
) tiene un rendimiento razonable, pero la consulta de AndriyM parece más eficiente ( 125 ms, 155 ms
). Ambos realizan una exploración secuencial de la tabla y no utilizan ningún índice.
En realidad, los resultados de la consulta de Jack son un poco mejores que los mostrados anteriormente (si eliminamos el order by
) y pueden mejorarse aún más eliminando los 4 internos distinct
y dejando solo el externo.
Finalmente, si, y solo si , los valores distintos de las 4 columnas son relativamente pocos, puede usar el WITH RECURSIVE
hack / optimización descrito en la página anterior de Análisis de índice suelto y usar los 4 índices, ¡con un resultado notablemente rápido! Probado con las mismas filas de 100K y aproximadamente 25 valores distintos distribuidos en las 4 columnas (¡se ejecuta en solo 2 ms!), Mientras que con 25K valores distintos es el más lento con 368 ms:
-- Query 3. (2 ms, 368ms)
WITH RECURSIVE
da AS (
SELECT min(a) AS n FROM observations
UNION ALL
SELECT (SELECT min(a) FROM observations
WHERE a > s.n)
FROM da AS s WHERE s.n IS NOT NULL ),
db AS (
SELECT min(b) AS n FROM observations
UNION ALL
SELECT (SELECT min(b) FROM observations
WHERE b > s.n)
FROM db AS s WHERE s.n IS NOT NULL ),
dc AS (
SELECT min(c) AS n FROM observations
UNION ALL
SELECT (SELECT min(c) FROM observations
WHERE c > s.n)
FROM dc AS s WHERE s.n IS NOT NULL ),
dd AS (
SELECT min(d) AS n FROM observations
UNION ALL
SELECT (SELECT min(d) FROM observations
WHERE d > s.n)
FROM db AS s WHERE s.n IS NOT NULL )
SELECT n
FROM
( TABLE da UNION
TABLE db UNION
TABLE dc UNION
TABLE dd
) AS x
WHERE n IS NOT NULL ;
SQLfiddle
Para resumir, cuando los valores distintos son pocos, la consulta recursiva es la ganadora absoluta, mientras que con muchos valores, mi segunda, las consultas de Jack (versión mejorada a continuación) y AndriyM son las de mejor desempeño.
Adiciones tardías, una variación en la primera consulta que, a pesar de las operaciones adicionales, funciona mucho mejor que la primera y solo un poco peor que la segunda:
-- Query 1b. (85 ms, 149 ms)
SELECT DISTINCT a AS n FROM observations
UNION
SELECT DISTINCT b FROM observations
UNION
SELECT DISTINCT c FROM observations
UNION
SELECT DISTINCT d FROM observations ;
y Jack ha mejorado:
-- Query 4b. (104 ms, 128 ms)
select distinct unnest( array_agg(a)||
array_agg(b)||
array_agg(c)||
array_agg(d) )
from t ;
SELECT a FROM tablename UNION SELECT b FROM tablename UNION SELECT c FROM tablename UNION SELECT d FROM tablename ;
?