sql >> Databasteknik >  >> RDS >> PostgreSQL

Hur matchar man element i en array av sammansatt typ?

Detta fungerar:

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

Eller mer omfattande, men föredraget :

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

Mer robust och undviker att utvärdera unnest() flera gånger. Se:

Detta fungerar också:

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

Kärnan i problemet är att IN ta en underfråga känner till två skilda former. Citerar handboken:

Din misslyckade fråga löser sig till den andra formen, medan du (förståeligt nog) förväntar dig den första. Men den andra formen gör detta:

Min första och andra fråga få det att fungera genom att dekomponera radtypen till höger om operatören. Så Postgres har tre bigint värderar vänster och höger och är nöjd.

Min tredje fråga får det att fungera genom att kapsla radtypen till vänster i en annan radkonstruktör . Postgres bryter bara ner den första nivån och slutar med en enda sammansatt typ - matchande den enstaka sammansatta typen till höger.

Observera att sökordet ROW krävs för det enskilda fältet vi lindar. Handboken:

Din arbetsfråga är subtilt annorlunda eftersom det ger en lista av värden till höger istället för en underfråga (ställ ). Det är en annan implementering som tar en annan kodväg. Den får till och med ett separat kapitel i manualen . Denna variant har ingen speciell behandling för en ROW-konstruktör till vänster. Så det fungerar precis som förväntat (av dig).

Mer likvärdiga (fungerande) syntaxvarianter med = 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[]);

Gäller även med (pk1, pk2, pk3)::element_pk_t eller ROW(pk1, pk2, pk3)::element_pk_t

Se:

Eftersom din källa är en array , Daniels andra fråga med (e.pk1, e.pk2, e.pk3) = ANY(c.elements) lämpar sig naturligt.
Men för en satsning på den snabbaste frågan , mina pengar är på min andra variant, eftersom jag förväntar mig att den använder PK-indexet optimalt.

Bara som proof of concept. Som a_horse kommenterade:en normaliserad DB-design kommer förmodligen att skala bäst.




  1. Hur man hämtar radvärden för en datagridview och skickar den till en annan form med en knapp i samma rad

  2. Dynamisk SQL returnerar "syntaxfel" på Google Apps Script med JDBC och MySQL

  3. fel:ORA-65096:ogiltigt vanligt användar- eller rollnamn i Oracle

  4. Hur man designar ett hierarkiskt rollbaserat passersystem