2009-09-23 9 views
9

Existe-t-il un moyen de forcer un ordre de jointure spécifique dans Postgres?Ordre de jointure de table dans postgres

J'ai une requête qui ressemble à ceci. J'ai éliminé un tas de choses qui étaient dans la vraie requête, mais cette simplification démontre le problème. Ce qui reste ne devrait pas être trop cryptique: En utilisant un système de sécurité de rôle/tâche, j'essaie de déterminer si un utilisateur donné a des privilèges pour effectuer une tâche donnée.

select task.taskid 
from userlogin 
join userrole using (userloginid) 
join roletask using (roleid) 
join task using (taskid) 
where loginname='foobar' 
and taskfunction='plugh' 

Mais je réalise que le programme connaît déjà la valeur de userlogin, il semblait donc la requête pourrait être plus efficace en sautant la recherche sur userlogin et remplir seulement dans le userloginid, comme ceci:

select task.taskid 
from userrole 
join roletask using (roleid) 
join task using (taskid) 
where userloginid=42 
and taskfunction='plugh' 

Lorsque j'ai fait cela - en éliminant une table de la requête et en codant en dur la valeur récupérée à partir de cette table à la place - le temps du plan expliquer a augmenté! Dans la requête d'origine, Postgres lit userlogin puis userrole puis roletask puis task. Mais dans la nouvelle requête, il a décidé de lire roletask en premier, puis de se joindre à userrole, même si cela nécessitait de faire un scan complet sur roletask.

complète expliquer les plans sont les suivants:

Version 1:

Hash Join (cost=12.79..140.82 rows=1 width=8) 
    Hash Cond: (roletask.taskid = task.taskid) 
    -> Nested Loop (cost=4.51..129.73 rows=748 width=8) 
     -> Nested Loop (cost=4.51..101.09 rows=12 width=8) 
       -> Index Scan using idx_userlogin_loginname on userlogin (cost=0.00..8.27 rows=1 width=8) 
        Index Cond: ((loginname)::text = 'foobar'::text) 
       -> Bitmap Heap Scan on userrole (cost=4.51..92.41 rows=33 width=16) 
        Recheck Cond: (userrole.userloginid = userlogin.userloginid) 
        -> Bitmap Index Scan on idx_userrole_login (cost=0.00..4.50 rows=33 width=0) 
          Index Cond: (userrole.userloginid = userlogin.userloginid) 
     -> Index Scan using idx_roletask_role on roletask (cost=0.00..1.50 rows=71 width=16) 
       Index Cond: (roletask.roleid = userrole.roleid) 
    -> Hash (cost=8.27..8.27 rows=1 width=8) 
     -> Index Scan using idx_task_taskfunction on task (cost=0.00..8.27 rows=1 width=8) 
       Index Cond: ((taskfunction)::text = 'plugh'::text) 

Version 2:

Hash Join (cost=96.58..192.82 rows=4 width=8) 
    Hash Cond: (roletask.roleid = userrole.roleid) 
    -> Hash Join (cost=8.28..104.10 rows=9 width=16) 
     Hash Cond: (roletask.taskid = task.taskid) 
     -> Seq Scan on roletask (cost=0.00..78.35 rows=4635 width=16) 
     -> Hash (cost=8.27..8.27 rows=1 width=8) 
       -> Index Scan using idx_task_taskfunction on task (cost=0.00..8.27 rows=1 width=8) 
        Index Cond: ((taskfunction)::text = 'plugh'::text) 
    -> Hash (cost=87.92..87.92 rows=31 width=8) 
     -> Bitmap Heap Scan on userrole (cost=4.49..87.92 rows=31 width=8) 
       Recheck Cond: (userloginid = 42) 
       -> Bitmap Index Scan on idx_userrole_login (cost=0.00..4.49 rows=31 width=0) 
        Index Cond: (userloginid = 42) 

(Oui, je sais que dans les deux cas, les coûts sont faibles et la différence doesn On dirait que ce serait important, mais c'est après avoir éliminé un tas de travail supplémentaire de la requête pour simplifier ce que j'ai à poster.La vraie question n'est toujours pas scandaleuse, mais je suis plus intéressé par t . Il principe)

+2

Pouvez-vous afficher les plans de requête (expliquer l'analyse) et les définitions de table? – hgmnz

+0

D'accord, vous avez demandé, j'ai remplacé l'exemple hypothétique simple par la requête réelle, et j'ai ajouté les résultats du plan d'explication. Oh, je suis sûr que je pourrais ajouter quelques index supplémentaires pour accélérer la deuxième requête, mais ce n'est pas le point. Pourquoi Postgres a-t-il choisi un plan inférieur à ce qu'il pouvait faire de mieux, compte tenu des requêtes qu'il avait? Surtout quand il a démontré qu'il pourrait faire mieux si je compliquais la requête? – Jay

+0

comparez-vous les temps d'exécution réels des requêtes ou simplement le coût global dans le plan d'explication? les coûts? notamment, vous venez de poster une sortie d'explication, pas d'expliquer l'analyse. Bien que vous vous attendiez à ce qu'un coût plus élevé équivaille à une exécution de requête plus lente, il se peut que cela ne fonctionne pas de cette façon. – araqnid

Répondre

4

Cette page dans la documentation décrit comment empêcher l'optimiseur de PostgreSQL à partir réordonnancement tables jointes, vous permettant de contrôler l'ordre de vous-même rejoint:

http://www.postgresql.org/docs/current/interactive/explicit-joins.html

+1

PostgreSQL a vraiment la meilleure documentation que j'ai vue sur un SGBDR. – hgmnz

+1

Avez-vous essayé cela? Je ne veux certainement pas changer ce paramètre pour toutes les requêtes, juste pour un ou deux ici et là. Si je le change avec une instruction set, est-ce que cela affecte le moteur de base de données entier, ou seulement la connexion actuelle, ou la transaction en cours? Hmm, je suppose que je pourrais tester cela en ouvrant deux connexions, en le mettant à partir d'un, puis en voyant si expliquer les plans sur l'autre changement ... – Jay

+0

Je ne l'ai pas essayé. Vous avez raison de l'essayer vous-même dans une paire de sessions simultanées est le meilleur moyen d'être sûr. Doc peut être faux (bien que @hgiminez le fasse remarquer, rarement dans le doc de PostgreSQL). –

1

Êtes-vous sûr que vos statistiques de table sont à jour? Lorsque l'optimiseur basé sur les coûts de PostgreSQLs échoue avec de telles choses triviales, c'est un bon signe que quelque chose ne va vraiment pas avec les statistiques de la table. Il est préférable de corriger la cause première plutôt que de contourner l'optimiseur intégré car le problème apparaîtra inévitablement ailleurs.

Exécutez ANALYZE sur les tables affectées et vérifiez si PostgreSQL choisit un autre plan. S'il choisit encore quelque chose de stupide, il serait vraiment intéressant de voir les plans de requête. L'optimiseur ne fait pas la bonne chose est généralement considéré comme un bug.

+0

Oui. Après mes premiers résultats surprenants, j'ai relancé l'analyse sur ces tables, puis refait les plans d'explication, et les résultats étaient similaires. – Jay

+1

Cela semble un peu étrange. Quel est votre paramètre effective_cache_size? La valeur par défaut de 128 Mo peut entraîner des analyses séquentielles déraisonnables sur les tables de petite et moyenne taille. En outre, pour les bases de données fortement mises en cache, l'abaissement de random_page_cost pourrait être une bonne idée. –

Questions connexes