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
DISTINCT
iarray_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
SELECT
resultere 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 BY
i stedet for at samle dem falsk. Meneg_assoc.aid
er 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.