PostgreSQL
 sql >> Datenbank >  >> RDS >> PostgreSQL

LEFT OUTER JOIN auf Array-Spalte mit mehreren Werten

Ja, der Überlappungsoperator && könnte einen GIN-Index für Arrays verwenden . Sehr nützlich für Abfragen, um Zeilen mit einer bestimmten Person zu finden (1 ) unter einer Reihe von Akteuren:

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

Allerdings , ist die Logik Ihrer Abfrage umgekehrt und sucht nach allen Personen, die in den Arrays in eg_assoc aufgeführt sind . Ein GIN-Index ist nein hier helfen. Wir brauchen nur den btree Index der PK person.id .

Richtige Abfragen

Grundlagen:

Die folgenden Abfragen bewahren ursprüngliche Arrays genau wie angegeben , einschließlich möglicher doppelter Elemente und der ursprünglichen Reihenfolge der Elemente. Funktioniert für 1-dimensionale Arrays . Zusätzliche Dimensionen werden zu einer einzigen Dimension gefaltet. Es ist komplexer, mehrere Dimensionen beizubehalten (aber durchaus möglich):

WITH ORDINALITY in Postgres 9.4 oder höher

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 Abfragen

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<>fiddle hier mit ein paar Varianten.
Altes sqlfiddle

Feines Detail:Wenn eine Person nicht gefunden wird, wird sie einfach fallen gelassen. Beide Abfragen erzeugen ein leeres Array ('{}' ), wenn für das gesamte Array keine Person gefunden wird. Andere Abfragestile würden NULL zurückgeben . Ich habe der Geige Varianten hinzugefügt.

Korrelierte Unterabfragen

Für Postgres 8.4+ (wobei generate_subsrcipts() wurde eingeführt):

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;

Kann sogar in Postgres 9.3 immer noch die beste Leistung erbringen.
Das ARRAY Konstruktor ist schneller als array_agg() . Siehe:

Ihre fehlgeschlagene Abfrage

Die Abfrage von @a_horse scheint um die Arbeit zu erledigen, aber es ist unzuverlässig, irreführend, möglicherweise falsch und unnötig teuer.

  1. Proxy-Cross-Join aufgrund von zwei nicht zusammenhängenden Joins. Ein hinterhältiges Anti-Pattern. Siehe:

    Oberflächlich behoben mit DISTINCT in array_agg() um die erzeugten Duplikate zu eliminieren, aber das ist wirklich eine Schande. Es beseitigt auch Duplikate im Original weil es an dieser Stelle unmöglich ist, den Unterschied zu erkennen - was möglicherweise falsch ist.

  2. Der Ausdruck a_person.id = any(eg_assoc.actors) funktioniert , aber beseitigt Duplikate aus dem Ergebnis (passiert zweimal in dieser Abfrage), was falsch ist, sofern nicht anders angegeben.

  3. Die ursprüngliche Reihenfolge der Array-Elemente wird nicht beibehalten . Das ist generell schwierig. Aber es verschärft sich in dieser Abfrage, weil Akteure und Wohltäter multipliziert und wieder unterschieden werden, was garantiert beliebige Reihenfolge.

  4. Keine Spaltenaliase im äußeren SELECT führen zu doppelten Spaltennamen, was dazu führt, dass einige Clients fehlschlagen (funktionieren nicht ohne Aliase).

  5. min(actors) und min(benefactors) sind nutzlos. Normalerweise würde man die Spalten einfach zu GROUP BY hinzufügen anstatt sie vorzutäuschen. Aber eg_assoc.aid ist sowieso die PK-Spalte (die die gesamte Tabelle in GROUP BY abdeckt ), also ist das nicht einmal notwendig. Nur actors, benefactors .

Das Zusammenfassen des gesamten Ergebnisses ist zunächst Zeit- und Arbeitsverschwendung. Verwenden Sie eine intelligentere Abfrage, die die Basiszeilen nicht multipliziert, dann müssen Sie sie nicht wieder zusammenfassen.