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.
-
Proxy cross join a causa di due join non correlati. Un subdolo anti-modello. Vedi:
Risolto superficialmente con
DISTINCT
inarray_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. -
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. -
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.
-
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). -
min(actors)
emin(benefactors)
sono inutili. Normalmente si aggiungono semplicemente le colonne aGROUP BY
invece di falsificarli. Maeg_assoc.aid
è comunque la colonna PK (che copre l'intera tabella inGROUP BY
), quindi non è nemmeno necessario. Soloactors, 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.