sql >> Databasteknik >  >> RDS >> PostgreSQL

LEFT OUTER JOIN på arraykolumn med flera värden

Ja, överlappningsoperator && skulle kunna använda ett GIN-index på arrayer . Mycket användbart för frågor den här för att hitta rader med en given person (1 ) bland en rad skådespelare:

SELECT * FROM eg_assoc WHERE actors && '{1}'::int[]

Men , logiken i din fråga är tvärtom, letar efter alla personer som är listade i arrayerna i eg_assoc . Ett GIN-index är nej hjälp här. Vi behöver bara btree-indexet för PK person.id .

Rätta frågor

Grunderna:

Följande frågor bevarar ursprungliga arrayer exakt som givna , inklusive möjliga dubbletter av element och ursprunglig ordning av element. Fungerar för endimensionella arrayer . Ytterligare dimensioner viks till en enda dimension. Det är mer komplicerat att bevara flera dimensioner (men fullt möjligt):

WITH ORDINALITY i Postgres 9.4 eller senare

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 frågor

För 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<>fiol här med ett par varianter.
Gammal sqlfiddle

Subtil detalj:Om en person inte hittas släpps den bara. Båda dessa frågor genererar en tom array ('{}' ) om ingen person hittas för hela arrayen. Andra frågestilar skulle returnera NULL . Jag lade till varianter till fiolen.

Korrelerade underfrågor

För Postgres 8.4+ (där generate_subsrcipts() introducerades):

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 fortfarande prestera bäst, även i Postgres 9.3.
The ARRAY konstruktör är snabbare än array_agg() . Se:

Din misslyckade fråga

frågan från @a_horse verkar att göra jobbet, men det är opålitligt, vilseledande, potentiellt felaktigt och onödigt dyrt.

  1. Proxy cross join på grund av två orelaterade joins. Ett lömskt antimönster. Se:

    Fixat ytligt med DISTINCT i array_agg() för att eliminera de genererade dubbletterna, men det är verkligen att sätta läppstift på en gris. Det eliminerar också dubbletter i originalet eftersom det är omöjligt att se skillnad vid det här laget - vilket är potentiellt felaktigt.

  2. Uttrycket a_person.id = any(eg_assoc.actors) fungerar , men eliminerar dubbletter från resultatet (händer två gånger i den här frågan), vilket är fel om inget annat anges.

  3. Ursprunglig ordning av matriselement bevaras inte . Detta är knepigt i allmänhet. Men det förvärras i den här frågan, eftersom aktörer och välgörare multipliceras och görs åtskilda igen, vilket garanterar godtycklig ordning.

  4. Inga kolumnalias i den yttre SELECT resultera i dubbla kolumnnamn, vilket gör att vissa klienter misslyckas (fungerar inte i fiolen utan alias).

  5. min(actors) och min(benefactors) är värdelösa. Normalt skulle man bara lägga till kolumnerna i GROUP BY istället för att falska samman dem. Men eg_assoc.aid är PK-kolumnen ändå (som täcker hela tabellen i GROUP BY ), så det är inte ens nödvändigt. Bara actors, benefactors .

Att samla hela resultatet är bortkastad tid och ansträngning till att börja med. Använd en smartare fråga som inte multiplicerar basraderna, då behöver du inte aggregera dem igen.



  1. Samma inloggning på 2 webbplatser

  2. MySQL - kan jag begränsa den maximala tiden som tillåts för en fråga att köras?

  3. Övergång från mysql till PDO

  4. Det enklaste sättet att lägga till flera utrymmen till en sträng i MySQL – SPACE()