PostgreSQL
 sql >> Base de données >  >> RDS >> PostgreSQL

Comment faire correspondre les éléments d'un tableau de type composite ?

Cela fonctionne :

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

Ou plus verbeux, mais préférable :

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

Plus robuste et évite d'évaluer unnest() plusieurs fois. Voir :

Cela fonctionne aussi :

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

Le cœur du problème est que IN prendre une sous-requête connaît deux formes distinctes. Citant le manuel :

Votre requête ayant échoué se résout à la deuxième forme, alors que vous vous attendez (naturellement) à la première. Mais le deuxième formulaire fait ceci :

Ma première et deuxième requête faites-le fonctionner en décomposant le type de ligne à droite de l'opérateur. Donc Postgres a trois bigint valeurs à gauche et à droite et est satisfait.

Ma troisième requête le fait fonctionner en imbriquant le type de ligne à gauche dans un autre constructeur de lignes . Postgres ne décompose que le premier niveau et se retrouve avec un seul type composite - correspondant au type composite unique à droite.

Notez que le mot-clé ROW est requis pour le seul champ que nous enveloppons. Le manuel :

Votre requête de travail est subtilement différent car il fournit une liste de valeurs à droite au lieu d'une sous-requête (définir ). C'est une implémentation différente prenant un chemin de code différent. Il obtient même un chapitre séparé dans le manuel . Cette variante n'a pas de traitement spécial pour un constructeur ROW à gauche. Cela fonctionne donc comme prévu (par vous).

Plus de variantes de syntaxe équivalentes (fonctionnelles) avec = 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[]);

Valable aussi avec (pk1, pk2, pk3)::element_pk_t ou ROW(pk1, pk2, pk3)::element_pk_t

Voir :

Puisque votre source est un tableau , deuxième requête de Daniel avec (e.pk1, e.pk2, e.pk3) = ANY(c.elements) se prête naturellement.
Mais pour un pari sur la requête la plus rapide , mon pari est sur ma deuxième variante, car je m'attends à ce qu'elle utilise l'indice PK de manière optimale.

Tout comme une preuve de concept. Comme a_horse l'a commenté :une conception de base de données normalisée sera probablement la meilleure mise à l'échelle.