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

Wie werden Elemente in einem Array des zusammengesetzten Typs abgeglichen?

Das funktioniert:

SELECT *
FROM   element 
WHERE  (pk1, pk2, pk3) IN (SELECT (unnest(elements)).*
                           FROM   collection
                           WHERE  id = 1);

Oder ausführlicher, aber bevorzugt :

SELECT *
FROM   element 
WHERE  (pk1, pk2, pk3) IN (SELECT (e).*
                           FROM   collection c, unnest(c.elements) e
                           WHERE  c.id = 1);

Robuster und vermeidet die Auswertung von unnest() mehrmals. Siehe:

Das geht auch:

SELECT *
FROM   element 
WHERE  ROW((pk1, pk2, pk3)) IN (SELECT unnest(elements)
                                FROM   collection
                                WHERE  id = 1);

Der Kern des Problems ist, dass IN Unterabfrage ausführen kennt zwei getrennte Formen. Zitieren von dem Handbuch:

Ihre fehlgeschlagene Abfrage löst sich in die zweite Form auf, während Sie (verständlicherweise) die erste erwarten. Aber die zweite Form tut dies:

Meine erste und zweite Anfrage damit es funktioniert, indem Sie den Zeilentyp zerlegen rechts vom Betreiber. Postgres hat also drei bigint Werte links und rechts und ist zufrieden.

Meine dritte Abfrage funktioniert, indem der Zeilentyp links in einem anderen Zeilenkonstruktor . Postgres zerlegt nur die erste Ebene und endet mit einem einzigen zusammengesetzten Typ – passend zum einzelnen zusammengesetzten Typ rechts.

Beachten Sie, dass das Schlüsselwort ROW ist für das einzelne Feld erforderlich, das wir umschließen. Das Handbuch:

Ihre Arbeitsabfrage ist etwas anders, da es eine Liste bereitstellt von Werten rechts anstelle einer Unterabfrage (einstellen ). Das ist eine andere Implementierung, die einen anderen Codepfad nimmt. Es bekommt sogar ein separates Kapitel im Handbuch . Diese Variante hat keine spezielle Behandlung für einen ROW-Konstruktor auf der linken Seite. Es funktioniert also wie erwartet (von Ihnen).

Äquivalentere (funktionierende) Syntaxvarianten mit = ANY :

SELECT * FROM element 
WHERE (pk1, pk2, pk3) = ANY ('{"(1,2,3)","(2,3,4)"}'::element_pk_t[]);

SELECT * FROM element 
WHERE (pk1, pk2, pk3) = ANY (ARRAY[(1,2,3)::element_pk_t,(2,3,4)::element_pk_t]);

SELECT * FROM element 
WHERE (pk1, pk2, pk3) = ANY (ARRAY[(1,2,3),(2,3,4)]::element[]);

Auch gültig mit (pk1, pk2, pk3)::element_pk_t oder ROW(pk1, pk2, pk3)::element_pk_t

Siehe:

Da Ihre Quelle ein Array ist , Daniels zweite Abfrage mit (e.pk1, e.pk2, e.pk3) = ANY(c.elements) bietet sich natürlich an.
Aber für eine Wette auf die schnellste Abfrage , ich setze auf meine zweite Variante, weil ich mir erwarte, dass sie den PK-Index optimal nutzt.

Nur als Proof-of-Concept. Wie a_horse kommentierte:Ein normalisiertes DB-Design wird wahrscheinlich am besten skalieren.