sql >> Database teknologi >  >> RDS >> PostgreSQL

LEFT OUTER JOIN på matrixkolonne med flere værdier

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.

  1. Proxy cross join på grund af to ikke-relaterede joins. Et lusket anti-mønster. Se:

    Rettet overfladisk med DISTINCT i array_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.

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

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

  4. Ingen kolonnealiasser i den ydre SELECT resultere i duplikerede kolonnenavne, hvilket får nogle klienter til at fejle (fungerer ikke i violinen uden aliaser).

  5. min(actors) og min(benefactors) er ubrugelige. Normalt ville man blot tilføje kolonnerne til GROUP BY i stedet for at samle dem falsk. Men eg_assoc.aid er PK-kolonnen alligevel (dækker hele tabellen i GROUP BY ), så det er ikke engang nødvendigt. Bare actors, 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.



  1. hvad betyder uautoriseret bruger i MYSQL?

  2. Tilslut Android til serveren i gratis webhost

  3. Dvale - unik kolonnebegrænsning ignoreres

  4. Få opdateret værdi i MySQL i stedet for berørte rækker