PostgreSQL
 sql >> Base de Dados >  >> RDS >> PostgreSQL

LEFT OUTER JOIN na coluna da matriz com vários valores


Sim, o operador de sobreposição && poderia usar um índice GIN em arrays . Muito útil para consultas esta para encontrar linhas com uma determinada pessoa (1 ) entre uma série de atores:
SELECT * FROM eg_assoc WHERE actors && '{1}'::int[]

No entanto , a lógica da sua consulta é inversa, procurando por todas as pessoas listadas nos arrays em eg_assoc . Um índice GIN é não ajuda aqui. Só precisamos do índice btree do PK person.id .

Consultas adequadas


Fundamentos:

As consultas a seguir preservam as matrizes originais exatamente como fornecidas , incluindo possíveis elementos duplicados e ordem original dos elementos. Funciona para matrizes unidimensionais . Dimensões adicionais são dobradas em uma única dimensão. É mais complexo preservar várias dimensões (mas totalmente possível):

WITH ORDINALITY no Postgres 9.4 ou 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 aqui com algumas variantes.
Antigo sqlfiddle

Detalhe sutil:se uma pessoa não for encontrada, ela é simplesmente descartada. Ambas as consultas geram um matriz vazia ('{}' ) se nenhuma pessoa for encontrada para toda a matriz. Outros estilos de consulta retornariam NULL . Eu adicionei variantes ao violino.

Subconsultas correlacionadas


Para Postgres 8.4+ (onde generate_subsrcipts() foi introduzido):
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;

Ainda pode ter melhor desempenho, mesmo no Postgres 9.3.
O ARRAY construtor é mais rápido que array_agg() . Ver:

Sua consulta falhou


A
consulta fornecida por @a_horse parece para fazer o trabalho, mas não é confiável, enganoso, potencialmente incorreto e desnecessariamente caro.

  1. Junção cruzada de proxy devido a duas junções não relacionadas. Um anti-padrão sorrateiro. Ver:

    Corrigido superficialmente com DISTINCT em array_agg() to elimina as duplicatas geradas, mas isso é realmente colocar batom em um porco. Também elimina duplicatas no original porque é impossível dizer a diferença neste momento - o que é potencialmente incorreto.

  2. A expressão a_person.id = any(eg_assoc.actors) funciona , mas elimina duplicatas do resultado (acontece duas vezes nesta consulta), que está errado, a menos que especificado.

  3. A ordem original dos elementos da matriz não foi preservada . Isso é complicado em geral. Mas é agravado nesta consulta, porque os atores e benfeitores são multiplicados e tornados distintos novamente, o que garantia ordem arbitrária.

  4. Nenhum alias de coluna no SELECT externo resultar em nomes de colunas duplicados, o que faz com que alguns clientes falhem (não funcionam no violino sem aliases).

  5. min(actors) e min(benefactors) são inúteis. Normalmente, basta adicionar as colunas a GROUP BY em vez de agregá-los falsamente. Mas eg_assoc.aid é a coluna PK de qualquer maneira (cobrindo toda a tabela em GROUP BY ), então isso nem é necessário. Apenas actors, benefactors .

Agregar todo o resultado é perda de tempo e esforço para começar. Use uma consulta mais inteligente que não multiplique as linhas de base, para que você não precise agregá-las novamente.