Ja, de overlappingsoperator &&
zou een GIN-index op arrays kunnen gebruiken
. Zeer handig voor vragen, deze om rijen met een bepaalde persoon te vinden (1
) tussen een reeks acteurs:
SELECT * FROM eg_assoc WHERE actors && '{1}'::int[]
Echter , de logica van uw zoekopdracht is andersom, op zoek naar alle personen die in de arrays in eg_assoc
staan vermeld . Een GIN-index is nee hier helpen. We hebben alleen de btree-index nodig van de PK person.id
.
Juiste zoekopdrachten
Basis:
De volgende zoekopdrachten behouden originele arrays precies zoals opgegeven , inclusief mogelijke dubbele elementen en originele volgorde van elementen. Werkt voor 1-dimensionale arrays . Extra dimensies worden samengevouwen tot één dimensie. Het is ingewikkelder om meerdere dimensies te behouden (maar volledig mogelijk):
WITH ORDINALITY
in Postgres 9.4 of hoger
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
vragen
Voor 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 hier
met een aantal varianten.
Oude sqlfiddle
Subtiel detail:als een persoon niet wordt gevonden, wordt deze gewoon gedropt. Beide zoekopdrachten genereren een lege array ('{}'
) als er geen persoon wordt gevonden voor de hele array. Andere zoekstijlen zouden NULL
. opleveren . Ik heb varianten aan de viool toegevoegd.
Gecorreleerde subquery's
Voor Postgres 8.4+ (waar generate_subsrcipts()
werd geïntroduceerd):
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 nog steeds het beste presteren, zelfs in Postgres 9.3.
De ARRAY
constructeur
is sneller dan array_agg()
. Zie:
Uw mislukte zoekopdracht
De query geleverd door @a_horse lijkt om het werk te doen, maar het is onbetrouwbaar, misleidend, mogelijk onjuist en onnodig duur.
-
Proxy cross join vanwege twee niet-gerelateerde joins. Een stiekem anti-patroon. Zie:
Oppervlakkig opgelost met
DISTINCT
inarray_agg()
om de gegenereerde duplicaten te elimineren, maar dat is echt lippenstift op een varken. Het elimineert ook duplicaten in het origineel omdat het op dit moment onmogelijk is om het verschil te zien - wat mogelijk onjuist is. -
De uitdrukking
a_person.id = any(eg_assoc.actors)
werkt , maar elimineert duplicaten van het resultaat (komt twee keer voor in deze zoekopdracht), wat fout is, tenzij gespecificeerd. -
Oorspronkelijke volgorde van array-elementen blijft niet behouden . Dit is over het algemeen lastig. Maar het wordt verergerd in deze vraag, omdat actoren en weldoeners worden vermenigvuldigd en weer onderscheiden, wat garandeert willekeurige volgorde.
-
Geen kolomaliassen in de buitenste
SELECT
resulteren in dubbele kolomnamen, waardoor sommige clients falen (niet werkend in de viool zonder aliassen). -
min(actors)
enmin(benefactors)
zijn nutteloos. Normaal gesproken zou je de kolommen gewoon toevoegen aanGROUP BY
in plaats van ze nep samen te voegen. Maareg_assoc.aid
is sowieso de PK-kolom (dekt de hele tabel inGROUP BY
), dus dat is niet eens nodig. Gewoonactors, benefactors
.
Het samenvoegen van het hele resultaat is in het begin verspilde tijd en moeite. Gebruik een slimmere zoekopdracht die de basisrijen niet vermenigvuldigt, dan hoeft u ze niet weer op te tellen.