sql >> Database >  >> RDS >> PostgreSQL

LEFT OUTER JOIN op matrixkolom met meerdere waarden

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.

  1. Proxy cross join vanwege twee niet-gerelateerde joins. Een stiekem anti-patroon. Zie:

    Oppervlakkig opgelost met DISTINCT in array_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.

  2. 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.

  3. 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.

  4. Geen kolomaliassen in de buitenste SELECT resulteren in dubbele kolomnamen, waardoor sommige clients falen (niet werkend in de viool zonder aliassen).

  5. min(actors) en min(benefactors) zijn nutteloos. Normaal gesproken zou je de kolommen gewoon toevoegen aan GROUP BY in plaats van ze nep samen te voegen. Maar eg_assoc.aid is sowieso de PK-kolom (dekt de hele tabel in GROUP BY ), dus dat is niet eens nodig. Gewoon actors, 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.



  1. Code dekkingsstatistieken

  2. Hoe TO_SECONDS() werkt in MariaDB

  3. Hoe de banen in orakel uit te voeren?

  4. Hex-waarde invoegen in MySQL