2009-09-23 19 views
9

¿Hay alguna manera de forzar una orden de unión específica en Postgres?Orden de unión de tabla en postgres

Tengo una consulta que se parece a esto. Eliminé un montón de cosas que estaban en la consulta real, pero esta simplificación demuestra el problema. Lo que queda no debe ser demasiado críptico: al usar un sistema de seguridad de roles/tareas, estoy tratando de determinar si un usuario dado tiene privilegios para realizar una tarea determinada.

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

pero me di cuenta que el programa ya conoce el valor de userlogin, por lo que parecía la consulta podría ser más eficiente por saltarse la búsqueda en userlogin y simplemente rellenando el userloginid, así:

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

Cuando lo hice, eliminando una tabla de la consulta y codificando el valor recuperado de esa tabla en su lugar, ¡el tiempo del plan de explicación se incrementó! En la consulta original, Postgres leyó userlogin luego userrole luego roletask then task. Pero en la nueva consulta, decidió leer roletask primero y luego unirse a userrole, aunque esto requería hacer un escaneo de archivo completo en roletask.

completa explicar los planes son:

Versión 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) 

Versión 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) 

(Sí, ya sé que en ambos casos los costos son bajos y la diferencia doesn Parece que no importaría, pero esto es después de que eliminé un montón de trabajo adicional de la consulta para simplificar lo que tengo que publicar. La consulta real aún no es escandalosa, pero estoy más interesado en t . El principio)

+2

¿Puede mostrar los planes de consulta (explicar el análisis) y las definiciones de tabla? – hgmnz

+0

Bien, preguntaste, reemplacé el ejemplo simple hipotético con la consulta real y agregué los resultados del plan de explicación. Oh, estoy seguro de que podría agregar algunos índices adicionales para acelerar la segunda consulta, pero ese no es el punto. ¿Por qué Postgres eligió un plan que era menos que lo mejor que podía hacer, dadas las consultas que tenía? ¿Especialmente cuando demostró que podría mejorar si hiciera la consulta más complicada? – Jay

+0

¿está comparando los tiempos de ejecución reales de las consultas o simplemente mirando el costo del título en el plan de explicación? ¿los costos? en particular, acaba de publicar la salida de explicación, no explicar analizar. aunque podría esperar un costo mayor para igualar a una ejecución de consulta más lenta, podría no funcionar de esa manera. – araqnid

Respuesta

4

Esta página en la documentación describe cómo evitar que el optimizador PostgreSQL desde la reordenación de las tablas combinadas, lo que le permite controlar el orden de combinaciones mismo:

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

+1

PostgreSQL realmente tiene la mejor documentación que he visto de un RDBMS. – hgmnz

+1

¿Has probado esto? Ciertamente no quiero cambiar esta configuración para todas las consultas, solo para una o dos aquí y allá. Si lo cambio con una declaración establecida, ¿eso afecta el motor de base de datos completo, o solo la conexión actual, o la transacción actual? Hmm, supongo que podría probar esto abriendo dos conexiones, configurándola desde una, y luego viendo si explico planes en la otra ... – Jay

+0

No lo he intentado. Tienes razón en que intentarlo tú mismo en un par de sesiones simultáneas es la mejor manera de estar seguro. Doc podría estar equivocado (aunque como señala @hgiminez, rara vez lo hace en el documento de PostgreSQL). –

1

¿Estás seguro de que sus estadísticas de la tabla están actualizados? Cuando el optimizador basado en el costo de PostgreSQL falla con cosas tan triviales, es una buena señal de que algo está muy mal con las estadísticas de la tabla. Es mejor solucionar la causa raíz que solucionar el problema anulando el optimizador incorporado porque el problema aparecerá inevitablemente en otro lugar también.

Ejecute ANALYZE en las tablas afectadas y vea si hace que PostgreSQL seleccione un plan diferente. Si aún elige algo tonto, sería muy interesante ver los planes de consulta. El optimizador que no está haciendo lo correcto generalmente se considera un error.

+0

Sí. Después de mis primeros resultados sorprendentes, volví a ejecutar el análisis en esas tablas y luego volví a hacer los planes de explicación, y los resultados fueron similares. – Jay

+1

Eso parece un poco extraño. ¿Cuál es su configuración effective_cache_size? El 128M predeterminado podría conducir a escaneos secuenciales irrazonables en tablas pequeñas y medianas. Además, para las bases de datos con mucho almacenamiento en caché, la reducción de random_page_cost podría ser una buena idea. –

Cuestiones relacionadas