PostgreSQL
 sql >> Database >  >> RDS >> PostgreSQL

LEFT OUTER JOIN sulla colonna dell'array con più valori

Sì, il operatore di sovrapposizione && potrebbe utilizzare un indice GIN su array . Molto utile per le query questa per trovare righe con una determinata persona (1 ) tra una serie di attori:

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

Tuttavia , la logica della tua query è il contrario, cercando tutte le persone elencate negli array in eg_assoc . Un indice GIN è no aiuta qui. Abbiamo solo bisogno dell'indice btree del PK person.id .

Query corrette

Nozioni di base:

Le seguenti query conservano gli array originali esattamente come dati , inclusi eventuali elementi duplicati e ordine originale degli elementi. Funziona con array 1-dimensionali . Le dimensioni aggiuntive sono piegate in un'unica dimensione. È più complesso preservare più dimensioni (ma del tutto possibile):

WITH ORDINALITY in Postgres 9.4 o successivo

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 domande

Per 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<>violino qui con un paio di varianti.
Vecchio sqlfiddle

Dettagli sottili:se una persona non viene trovata, viene semplicemente abbandonata. Entrambe queste query generano un array vuoto ('{}' ) se non viene trovata alcuna persona per l'intero array. Altri stili di query restituirebbero NULL . Ho aggiunto delle varianti al violino.

Subquery correlate

Per Postgres 8.4+ (dove generate_subsrcipts() è stato introdotto):

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;

Può ancora funzionare al meglio, anche in Postgres 9.3.
The ARRAY costruttore è più veloce di array_agg() . Vedi:

La tua query non riuscita

La query fornita da @a_horse sembra per svolgere il lavoro, ma è inaffidabile, fuorviante, potenzialmente scorretto e inutilmente costoso.

  1. Proxy cross join a causa di due join non correlati. Un subdolo anti-modello. Vedi:

    Risolto superficialmente con DISTINCT in array_agg() per eliminare i duplicati generati, ma questo è davvero mettere il rossetto su un maiale. Inoltre elimina i duplicati nell'originale perché a questo punto è impossibile distinguere la differenza, il che è potenzialmente errato.

  2. L'espressione a_person.id = any(eg_assoc.actors) funziona , ma elimina i duplicati dal risultato (succede due volte in questa query), che è errato se non specificato.

  3. L'ordine originale degli elementi dell'array non è preservato . Questo è complicato in generale. Ma in questa domanda si aggrava, perché attori e benefattori vengono moltiplicati e resi nuovamente distinti, il che garantisce ordine arbitrario.

  4. Nessun alias di colonna nel SELECT esterno risulta in nomi di colonna duplicati, il che fa sì che alcuni client non funzionino (non funzionano nel violino senza alias).

  5. min(actors) e min(benefactors) sono inutili. Normalmente si aggiungono semplicemente le colonne a GROUP BY invece di falsificarli. Ma eg_assoc.aid è comunque la colonna PK (che copre l'intera tabella in GROUP BY ), quindi non è nemmeno necessario. Solo actors, benefactors .

Aggregare l'intero risultato è tempo e fatica sprecati per cominciare. Utilizza una query più intelligente che non moltiplichi le righe di base, quindi non devi aggregarle nuovamente.