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.
-
Unión cruzada de proxy debido a dos uniones no relacionadas. Un antipatrón furtivo. Ver:
Se corrigió superficialmente con
DISTINCT
enarray_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. -
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. -
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.
-
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). -
min(actors)
ymin(benefactors)
son inútiles Normalmente, uno simplemente agregaría las columnas aGROUP BY
en lugar de agregarlos falsamente. Peroeg_assoc.aid
es la columna PK de todos modos (cubriendo toda la tabla enGROUP BY
), por lo que ni siquiera es necesario. Soloactors, 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.