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

LEFT OUTER JOIN sur une colonne de tableau avec plusieurs valeurs

Oui, le opérateur de chevauchement && pourrait utiliser un index GIN sur les tableaux . Très utile pour les requêtes celle-ci pour trouver des lignes avec une personne donnée (1 ) parmi un panel d'acteurs :

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

Cependant , la logique de votre requête est inverse, recherchant toutes les personnes listées dans les tableaux dans eg_assoc . Un indice GIN est non aide ici. Nous avons juste besoin de l'index btree du PK person.id .

Requêtes appropriées

Notions de base :

Les requêtes suivantes préservent les tableaux d'origine exactement tels qu'ils sont donnés , y compris les éventuels éléments en double et l'ordre d'origine des éléments. Fonctionne pour les tableaux unidimensionnels . Les dimensions supplémentaires sont pliées en une seule dimension. Il est plus complexe de conserver plusieurs dimensions (mais tout à fait possible) :

WITH ORDINALITY dans Postgres 9.4 ou version ultérieure

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 requêtes

Pour 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<>violon ici avec quelques variantes.
Ancien sqlfiddle

Détail subtil :si une personne n'est pas retrouvée, elle est simplement abandonnée. Ces deux requêtes génèrent un tableau vide ('{}' ) si aucune personne n'est trouvée pour l'ensemble du tableau. D'autres styles de requête renverraient NULL . J'ai ajouté des variantes au violon.

Sous-requêtes corrélées

Pour Postgres 8.4+ (où generate_subsrcipts() a été introduit) :

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;

Peut toujours fonctionner mieux, même dans Postgres 9.3.
Le ARRAY constructeur est plus rapide que array_agg() . Voir :

Votre requête a échoué

La requête fournie par @a_horse semble pour faire le travail, mais il n'est pas fiable, trompeur, potentiellement incorrect et inutilement coûteux.

  1. Jointure croisée proxy en raison de deux jointures non liées. Un anti-modèle sournois. Voir :

    Corrigé superficiellement avec DISTINCT dans array_agg() pour éliminer les doublons générés, mais c'est vraiment mettre du rouge à lèvres sur un cochon. Cela élimine également les doublons dans l'original car il est impossible de faire la différence à ce stade - ce qui est potentiellement incorrect.

  2. L'expression a_person.id = any(eg_assoc.actors) fonctionne , mais élimine les doublons du résultat (se produit deux fois dans cette requête), ce qui est faux sauf indication contraire.

  3. L'ordre d'origine des éléments du tableau n'est pas conservé . C'est délicat en général. Mais c'est aggravé dans cette requête, car acteurs et bienfaiteurs se multiplient et se distinguent à nouveau, ce qui garantit ordre arbitraire.

  4. Aucun alias de colonne dans le SELECT externe entraîner des noms de colonnes en double, ce qui fait échouer certains clients (ne fonctionnant pas dans le violon sans alias).

  5. min(actors) et min(benefactors) sont inutiles. Normalement, on ajouterait simplement les colonnes à GROUP BY au lieu de les fausser en les agrégeant. Mais eg_assoc.aid est de toute façon la colonne PK (couvrant toute la table dans GROUP BY ), donc ce n'est même pas nécessaire. Juste actors, benefactors .

L'agrégation de l'ensemble du résultat est une perte de temps et d'efforts pour commencer. Utilisez une requête plus intelligente qui ne multiplie pas les lignes de base, vous n'avez alors pas à les agréger.