sql >> Base de Datos >  >> RDS >> PostgreSQL

UNIÓN EXTERNA IZQUIERDA en la columna de matriz con múltiples valores

Sí, el operador de superposición && podría usar un índice GIN en matrices . Muy útil para consultas esta para encontrar filas con una persona dada (1 ) entre una serie de actores:

SELECT * FROM eg_assoc WHERE actors && '{1}'::int[]

Sin embargo , la lógica de su consulta es al revés, buscando todas las personas enumeradas en las matrices en eg_assoc . Un índice GIN es no ayuda aquí. Solo necesitamos el índice btree del PK person.id .

Consultas adecuadas

Conceptos básicos:

Las siguientes consultas conservan las matrices originales exactamente como se dan , incluidos los posibles elementos duplicados y el orden original de los elementos. Funciona para matrices unidimensionales . Las dimensiones adicionales se pliegan en una sola dimensión. Es más complejo preservar múltiples dimensiones (pero totalmente posible):

WITH ORDINALITY en Postgres 9.4 o posterior

SELECT aid, actors
     , ARRAY(SELECT name
             FROM   unnest(e.actors) WITH ORDINALITY a(id, i)
             JOIN   eg_person p USING (id)
             ORDER  BY a.i) AS act_names
     , benefactors
     , ARRAY(SELECT name
             FROM   unnest(e.benefactors) WITH ORDINALITY b(id, i)
             JOIN   eg_person USING (id)
             ORDER  BY b.i) AS ben_names
FROM   eg_assoc e;

LATERAL consultas

Para PostgreSQL 9.3+ .

SELECT e.aid, e.actors, a.act_names, e.benefactors, b.ben_names
FROM   eg_assoc e
, LATERAL (
   SELECT ARRAY( SELECT name
                 FROM   generate_subscripts(e.actors, 1) i
                 JOIN   eg_person p ON p.id = e.actors[i]
                 ORDER  BY i)
   ) a(act_names)
, LATERAL (
   SELECT ARRAY( SELECT name
                 FROM   generate_subscripts(e.benefactors, 1) i
                 JOIN   eg_person p ON p.id = e.benefactors[i]
                 ORDER  BY i)
   ) b(ben_names);

db<>fiddle aquí con un par de variantes.
Antiguo sqlfiddle

Detalle sutil:si no se encuentra una persona, simplemente se descarta. Ambas consultas generan una matriz vacía ('{}' ) si no se encuentra ninguna persona para toda la matriz. Otros estilos de consulta devolverían NULL . Agregué variantes al violín.

Subconsultas correlacionadas

Para Postgres 8.4+ (donde generate_subsrcipts() fue introducido):

SELECT aid, actors
     , ARRAY(SELECT name
             FROM   generate_subscripts(e.actors, 1) i
             JOIN   eg_person p ON p.id = e.actors[i]
             ORDER  BY i) AS act_names
     , benefactors
     , ARRAY(SELECT name
             FROM   generate_subscripts(e.benefactors, 1) i
             JOIN   eg_person p ON p.id = e.benefactors[i]
             ORDER  BY i) AS ben_names
FROM   eg_assoc e;

Todavía puede funcionar mejor, incluso en Postgres 9.3.
El ARRAY constructor es más rápido que array_agg() . Ver:

Tu consulta fallida

La consulta proporcionada por @a_horse parece para hacer el trabajo, pero es poco confiable, engañoso, potencialmente incorrecto e innecesariamente costoso.

  1. Unión cruzada de proxy debido a dos uniones no relacionadas. Un antipatrón furtivo. Ver:

    Se corrigió superficialmente con DISTINCT en array_agg() para eliminar los duplicados generados, pero eso es realmente poner lápiz labial en un cerdo. También elimina duplicados en el original porque es imposible notar la diferencia en este punto, lo cual es potencialmente incorrecto.

  2. La expresión a_person.id = any(eg_assoc.actors) funciona , pero elimina los duplicados del resultado (ocurre dos veces en esta consulta), lo cual es incorrecto a menos que se especifique.

  3. El orden original de los elementos de la matriz no se conserva . Esto es complicado en general. Pero se agrava en esta consulta, porque actores y benefactores se multiplican y se vuelven a distinguir, lo que garantiza orden arbitrario.

  4. Sin alias de columna en el exterior SELECT dan como resultado nombres de columna duplicados, lo que hace que algunos clientes fallen (no funcionan en el violín sin alias).

  5. min(actors) y min(benefactors) son inútiles Normalmente, uno simplemente agregaría las columnas a GROUP BY en lugar de agregarlos falsamente. Pero eg_assoc.aid es la columna PK de todos modos (cubriendo toda la tabla en GROUP BY ), por lo que ni siquiera es necesario. Solo actors, benefactors .

Para empezar, agregar todo el resultado es una pérdida de tiempo y esfuerzo. Use una consulta más inteligente que no multiplique las filas base, luego no tendrá que volver a agregarlas.