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.
-
Proxy-Cross-Join aufgrund von zwei nicht zusammenhängenden Joins. Ein hinterhältiges Anti-Pattern. Siehe:
Oberflächlich behoben mit
DISTINCT
inarray_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. -
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. -
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.
-
Keine Spaltenaliase im äußeren
SELECT
führen zu doppelten Spaltennamen, was dazu führt, dass einige Clients fehlschlagen (funktionieren nicht ohne Aliase). -
min(actors)
undmin(benefactors)
sind nutzlos. Normalerweise würde man die Spalten einfach zuGROUP BY
hinzufügen anstatt sie vorzutäuschen. Abereg_assoc.aid
ist sowieso die PK-Spalte (die die gesamte Tabelle inGROUP BY
abdeckt ), also ist das nicht einmal notwendig. Nuractors, 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.