Contrôle du planner avec des JOIN explicites

Depuis PostgreSQL 7.1 il est possible de contrôler le planner de requête pour certaines extensions en utilisant la syntaxe explicite JOIN.

Une simple jointure de requête comme :

SELECT * FROM a,b,c WHERE a.id = b.id AND b.ref = c.id;
    
le planner est libre de joindre les tables dans n'importe quel ordre. Par exemple, il peut générer un plan de requête qui joint A à B, en utilisant la clause WHERE a.id = b.id, aet ensuite joindre C à cette table, en utilisant l'autre clause WHERE. Ou il peut joindre B à C et alors joindre A avec le même résultat. Ou il peut joindre A à C et alors les joindre à B --- mais ce serait inefficace, depuis que le produit cartésien de A et C devra être formé la clause WHERE étant non applicable pour permettre l'optimisation de la jointure. (Toutes les jointures de l'exécuteur de PostgreSQL se produisent entre deux entrées tables). Le point important est que ces différentes possibilités de jointure donnent des résultats sémantiquement équivalents mais peuvent avoir des coûts d'exécution très différents. Donc, le planner les explorera tous pour essayer de trouver le plan de requête le plus efficace.

Quand un plan de requête comprend deux ou trois tables, il n'y a pas à chercher plusieurs ordonnancements de jointures. Mais le nombre d'ordonnancements de jointures croît exponentiellement par rapport au nombre de tables. À partir de 10 tables il n'est pas pratique de faire une recherche exhaustive de toutes les possibilités, et même pour six ou sept tables le planning peut prendre beaucoup de temps. Quand il y a trop d'entrées tables, le planner de PostgreSQL permutera d'une recherche exhaustive en une recherche probabiliste génétique à travers un nombre de possibilités limitées. (Le seuil de permutation est est placé par le paramètre de lancement GEQO_THRESHOLD décrit dans le Guide de l'administrateur). La recherche génétique prend moins de temps, mais ne trouvera pas nécessairement le meilleur plan de requête possible.

Quand la requête comprend des jointures externes, le planner a moins de liberté que pour les jointures internes (pleines). Par exemple,

SELECT * FROM a LEFT JOIN (b JOIN c ON (b.ref = c.id)) ON (a.id = b.id);
    
Bien que ces restrictions de requête semblent, de façon superficielle, similaires à l'exemple précédent, les sémantiques sont différentes car une ligne doit être émise pour chaque ligne de A qui n'a pas de ligne appariée dans la jointure de B et C. Donc, le planner n'a pas le choix de l'ordonnancement de jointure ici : il doit joindre B et C et ensuite joindre A à ce résultat. Par conséquent, cette requête prend moins de temps pour le plan que la requête précédente.

Depuis PostgreSQL 7.1, le planner traite toutes les syntaxes explicites JOIN comme contraignant l'ordonnancement de jointure, même si ce n'est pas logiquement nécessaire pour faire une contrainte pour des jointures internes. Donc, bien que toutes ces requêtes donnent le même résultat :

SELECT * FROM a,b,c WHERE a.id = b.id AND b.ref = c.id;
SELECT * FROM a CROSS JOIN b CROSS JOIN c WHERE a.id = b.id AND b.ref = c.id;
SELECT * FROM a JOIN (b JOIN c ON (b.ref = c.id)) ON (a.id = b.id);
    
la seconde et la troisième prennent moins de temps que la première. Cet effet ne vaut pas vraiment le coup pour seulement trois tables, mais peut être un sauveur avec de nombreuses tables.

Vous n'avez pas besoin de contraindre l'ordonnancement de jointure complètement afin de réduire le temps de recherche, car utiliser les opérateurs de jointure fonctionne dans une liste FROM. Par exemple,

SELECT * FROM a CROSS JOIN b, c, d, e WHERE ...;
    
force le planner à joindre A à B avant de les joindre aux autres tables, mais ne contraint pas ses choix autrement. Dans cet exemple, le nombre d'ordonnancement de jointure possibles est réduit d'un facteur 5.

Si vous avez un mélange de jointures internes et externes dans une requête complexe, vous pouvez ne pas vouloir contraindre la recherche du planner pour un bon ordonnancement de jointures internes dans une jointure externe. Vous ne pouvez faire ceci directement dans la syntaxe JOIN, mais pouvez obtenir une limitation syntaxique en utilisant les sous sélections. Par exemple,

SELECT * FROM d LEFT JOIN
        (SELECT * FROM a, b, c WHERE ...) AS ss
        ON (...);
    
Ici, joindre D doit être la dernière étape du plan de requête, mais le planner est libre de considérer divers ordonnancements de jointure pour A, B et C.

Contraindre la recherche du planner de cette façon est techniquement pratique pour réduire le temps de planning et pour diriger le planner vers un bon plan de requête. Si le planner choisi un mauvais ordonnancement de jointure par défaut, vous pouvez le forcer à choisir un meilleur ordonnancement par la syntaxe JOIN --- en supposant que vous connaissez un meilleur ordonnancement. L'expérimentation est recommandée.