Ja, overlapningsoperatør && kunne bruge et GIN-indeks på arrays
. Meget nyttigt for forespørgsler denne for at finde rækker med en given person (1 ) blandt en række skuespillere:
SELECT * FROM eg_assoc WHERE actors && '{1}'::int[]
Men , logikken i din forespørgsel er den anden vej rundt, og leder efter alle personer, der er anført i arrays i eg_assoc . Et GIN-indeks er nej hjælp her. Vi mangler bare btree-indekset for PK person.id .
Korrekte forespørgsler
Grundlæggende:
Følgende forespørgsler bevarer originale arrays nøjagtigt som givet , herunder mulige duplikerede elementer og original rækkefølge af elementer. Virker til 1-dimensionelle arrays . Yderligere dimensioner er foldet til en enkelt dimension. Det er mere komplekst at bevare flere dimensioner (men helt muligt):
WITH ORDINALITY i Postgres 9.4 eller nyere
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 forespørgsler
Til 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 her
med et par varianter.
Gamle sqlfiddle
Subtile detaljer:Hvis en person ikke bliver fundet, bliver den bare droppet. Begge disse forespørgsler genererer et tomt array ('{}' ), hvis ingen person findes for hele arrayet. Andre forespørgselsstile ville returnere NULL . Jeg tilføjede varianter til violinen.
Korrelerede underforespørgsler
Til Postgres 8.4+ (hvor generate_subsrcipts()
blev indført):
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;
Kan stadig præstere bedst, selv i Postgres 9.3.
The ARRAY konstruktør
er hurtigere end array_agg() . Se:
Din mislykkede forespørgsel
forespørgslen leveret af @a_horse synes at udføre arbejdet, men det er upålideligt, vildledende, potentielt forkert og unødvendigt dyrt.
-
Proxy cross join på grund af to ikke-relaterede joins. Et lusket anti-mønster. Se:
Rettet overfladisk med
DISTINCTiarray_agg()at eliminere de genererede dubletter, men det er virkelig at sætte læbestift på en gris. Det eliminerer også dubletter i originalen fordi det er umuligt at kende forskel på dette tidspunkt - hvilket potentielt er forkert. -
Udtrykket
a_person.id = any(eg_assoc.actors)virker , men eliminerer dubletter fra resultatet (sker to gange i denne forespørgsel), hvilket er forkert, medmindre det er angivet. -
Den oprindelige rækkefølge af array-elementer er ikke bevaret . Dette er generelt vanskeligt. Men det forværres i denne forespørgsel, fordi aktører og velgørere multipliceres og gøres adskilte igen, hvilket garanter vilkårlig rækkefølge.
-
Ingen kolonnealiasser i den ydre
SELECTresultere i duplikerede kolonnenavne, hvilket får nogle klienter til at fejle (fungerer ikke i violinen uden aliaser). -
min(actors)ogmin(benefactors)er ubrugelige. Normalt ville man blot tilføje kolonnerne tilGROUP BYi stedet for at samle dem falsk. Meneg_assoc.aider PK-kolonnen alligevel (dækker hele tabellen iGROUP BY), så det er ikke engang nødvendigt. Bareactors, benefactors.
At samle hele resultatet er spildt tid og kræfter til at begynde med. Brug en smartere forespørgsel, der ikke multiplicerer basisrækkerne, så behøver du ikke aggregere dem igen.