Supongamos que tenemos una tabla con cuatro columnas (a,b,c,d)
del mismo tipo de datos.
¿Es posible seleccionar todos los valores distintos dentro de los datos en las columnas y devolverlos como una sola columna o tengo que crear una función para lograr esto?
postgresql
postgresql-performance
postgresql-9.4
distinct
Fabrizio Mazzoni
fuente
fuente
SELECT a FROM tablename UNION SELECT b FROM tablename UNION SELECT c FROM tablename UNION SELECT d FROM tablename ;
?UNION
Respuestas:
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):Otro sería primero
UNION ALL
y luego usarDISTINCT
. 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: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 internosdistinct
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: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:
y Jack ha mejorado:
fuente
Puede usar LATERAL, como en esta consulta :
La palabra clave LATERAL permite que el lado derecho de la unión haga referencia a objetos desde el lado izquierdo. En este caso, el lado derecho es un constructor VALUES que construye un subconjunto de una sola columna a partir de los valores de columna que desea colocar en una sola columna. La consulta principal simplemente hace referencia a la nueva columna y también le aplica DISTINCT.
fuente
Para ser claros, lo usaría
union
como sugiere ypercube , pero también es posible con matrices:dbfiddle aquí
fuente
Más corto
Una versión menos detallada de la idea de Andriy es solo un poco más larga, pero más elegante y más rápida.
Para muchos valores duplicados distintos / pocos :
Lo más rápido
¡Con un índice en cada columna involucrada!
Para algunos valores duplicados distintos / muchos :
Esta es otra variante de rCTE, similar a la que @ypercube ya publicó , pero la uso en
ORDER BY 1 LIMIT 1
lugar de lamin(a)
cual suele ser un poco más rápida. Tampoco necesito un predicado adicional para excluir valores NULL.Y en
LATERAL
lugar de una subconsulta correlacionada, porque es más limpia (no necesariamente más rápida).Explicación detallada en mi respuesta para esta técnica:
Actualicé el Fiddle de SQL de ypercube y agregué el mío a la lista de reproducción.
fuente
EXPLAIN (ANALYZE, TIMING OFF)
para verificar el mejor rendimiento general? (Lo mejor de 5 para excluir los efectos de almacenamiento en caché.)VALUES ...
es más rápida queunnest(ARRAY[...])
.LATERAL
está implícito para las funciones de devolución de conjuntos en laFROM
lista.Puede, pero mientras escribía y probaba la función me sentí mal. Es un desperdicio de recursos.
Solo use una unión y más selectos. Única ventaja (si lo es), un solo escaneo desde la tabla principal.
En sql fiddle, debe cambiar el separador de $ a otra cosa, como /
fuente