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

Quelle est la différence entre LATERAL JOIN et une sous-requête dans PostgreSQL ?

C'est quoi un LATERAL rejoindre ?

La fonctionnalité a été introduite avec PostgreSQL 9.3. Le manuel :

Sous-requêtes apparaissant dans FROM peut être précédé du mot clé LATERAL . Cela leur permet de référencer les colonnes fournies par FROM précédent éléments. (Sans LATERAL , chaque sous-requête est évaluée indépendamment et ne peut donc pas renvoyer à un autre FROM article.)

Fonctions de table apparaissant dans FROM peut également être précédé du mot-clé LATERAL , mais pour les fonctions le mot clé est facultatif; les arguments de la fonction peuvent contenir des références aux colonnes fournies par FROM précédent articles dans tous les cas.

Des exemples de code de base y sont donnés.

Plus comme un corrélé sous-requête

A LATERAL join ressemble plus à une sous-requête corrélée, pas à une sous-requête simple, dans la mesure où les expressions à droite d'un LATERAL join sont évalués une fois pour chaque ligne restante - tout comme un élément corrélé sous-requête - alors qu'une sous-requête simple (expression de table) est évaluée une fois seul. (Le planificateur de requêtes a cependant des moyens d'optimiser les performances pour l'un ou l'autre.)
Réponse associée avec des exemples de code pour les deux côte à côte, résolvant le même problème :

  • Optimiser la requête GROUP BY pour récupérer la dernière ligne par utilisateur

Pour renvoyer plusieurs colonnes , un LATERAL join est généralement plus simple, plus propre et plus rapide.
N'oubliez pas non plus que l'équivalent d'une sous-requête corrélée est LEFT JOIN LATERAL ... ON true :

  • Appeler plusieurs fois une fonction renvoyant un ensemble avec un argument de tableau

Choses qu'une sous-requête ne peut pas faire

Il y a choses qu'un LATERAL join peut le faire, mais une sous-requête (corrélée) ne le peut pas (facilement). Une sous-requête corrélée ne peut renvoyer qu'une seule valeur, pas plusieurs colonnes ni plusieurs lignes - à l'exception des appels de fonction nus (qui multiplient les lignes de résultats s'ils renvoient plusieurs lignes). Mais même certaines fonctions de retour d'ensemble ne sont autorisées que dans le FROM clause. Comme unnest() avec plusieurs paramètres dans Postgres 9.4 ou version ultérieure. Le manuel :

Ceci n'est autorisé que dans le FROM clause ;

Cela fonctionne donc, mais ne peut pas (facilement) être remplacé par une sous-requête :

CREATE TABLE tbl (a1 int[], a2 int[]);
SELECT * FROM tbl, unnest(a1, a2) u(elem1, elem2);  -- implicit LATERAL

La virgule (, ) dans le FROM la clause est une notation courte pour CROSS JOIN .
LATERAL est supposé automatiquement pour les fonctions de table.
À propos du cas particulier de UNNEST( array_expression [, ... ] ) :

  • Comment déclarez-vous qu'une fonction set-returning n'est autorisée que dans la clause FROM ?

Fonctions de retour de set dans le SELECT liste

Vous pouvez également utiliser des fonctions de retour d'ensemble comme unnest() dans le SELECT liste directement. Cela présentait un comportement surprenant avec plus d'une fonction de ce type dans le même SELECT liste jusqu'à Postgres 9.6. Mais il a finalement été aseptisé avec Postgres 10 et est maintenant une alternative valable (même si ce n'est pas le SQL standard). Voir :

  • Quel est le comportement attendu pour plusieurs fonctions renvoyant des ensembles dans la clause SELECT ?

S'appuyant sur l'exemple ci-dessus :

SELECT *, unnest(a1) AS elem1, unnest(a2) AS elem2
FROM   tbl;

Comparaison :

dbfiddle pour la page 9.6 ici
dbfiddle pour la page 10 ici

Clarifier les informations erronées

Le manuel :

Pour le INNER et OUTER types de jointure, une condition de jointure doit être spécifiée, à savoir exactement l'une des valeurs NATURAL , ON condition_jointure , ou USING (join_column [, ...]). Voir ci-dessous pour la signification.
Pour CROSS JOIN , aucune de ces clauses ne peut apparaître.

Donc ces deux requêtes sont valides (même si elles ne sont pas particulièrement utiles) :

SELECT *
FROM   tbl t
LEFT   JOIN LATERAL (SELECT * FROM b WHERE b.t_id = t.t_id) t ON TRUE;

SELECT *
FROM   tbl t, LATERAL (SELECT * FROM b WHERE b.t_id = t.t_id) t;

Alors que celui-ci n'est pas :

SELECT *
FROM   tbl t
LEFT   JOIN LATERAL (SELECT * FROM b WHERE b.t_id = t.t_id) t;

C'est pourquoi l'exemple de code d'Andomar est correct (le CROSS JOIN ne nécessite pas de condition de jointure) et is d'Attila ne l'était pas.