2012-01-27 13 views
5

Tengo un problema con una consulta relativamente simple y el plan de ejecución de acceso elige para ello.¿Cómo puedo hacer que MS-Access elija un plan de ejecución diferente/correcto para mi consulta

la consulta es de esta forma

SELECT somethings 
FROM A INNER JOIN (B INNER JOIN (C INNER JOIN D ON ...) ON ...) ON ... 
WHERE A.primaryKey= 1 AND D.d = 2; 

C y D tienen relativamente pocas filas. A y B tienen algunas miles filas.

La consulta, que devuelve 2 filas (no estoy seguro si esto es pertinente) es realmente lenta. Se ejecuta en 17 segundos. Si elimino la parte AND D.d = 2 de la cláusula where, la consulta ahora devuelve 4 filas y se ejecuta instantáneamente.

Por lo tanto, entiendo que el motor JET podría ejecutar la consulta sin el filtro en D.d al instante, luego ejecutar dicho filtro al instante (solo 4 filas para filtrar). Por lo tanto, no debería ser demasiado tiempo para ejecutar la consulta con el filtro D.d = 2.

Intenté crear una sub consulta sin el filtro e incluir esto en otra consulta que solo filtraría el resultado, pero aún es lento. Supongo que el motor JET es lo suficientemente inteligente como para "aplanar" las consultas secundarias, por lo que el resultado es el mismo.

Como no pude ejecutar la consulta como deseaba, utilicé el objeto JETSHOWPLAN para que Access publicara su plan de ejecución. Esto es lo que encontré:

Para la consulta rápida (la que no tiene D.d = 2), el primer paso del plan de consulta es aplicar el filtro A.primaryKey = 1 en la tabla A. Esto da como resultado un conjunto de datos de 1 fila de más de 30000. Luego, las uniones parecen ejecutarse de A a D usando un índice con un conjunto de datos que nunca supera las 4 filas.

Parece que la consulta lenta se ejecuta en el orden inverso. D y C se unen primero, luego se prueba D.d = 2. Después de eso, se ejecutan las uniones de C a A. De esta manera, los datos que se deben unir de D a C, de C a B y de B a A son mucho más grandes. Cuando se ejecutan todos los JOIN y antes de que se ejecute A.primaryKey=1, el conjunto de datos tendrá 120,000 filas.

¿Hay alguna manera de forzar el plan de consulta correcto en Access?

Espero haber sido claro. Avíseme si debería publicar los planes de consulta. No lo hice porque son bastante grandes.

Gracias de antemano,

MP

+2

Puesto que no puede proporcionar pistas para el planificador de consulta, me sospecha que eres SOL. Si esto es crítico para el rendimiento, puede adjuntar la parte rápida de la consulta en una tabla borrador y usarla para otra consulta 'D.d = 2'. Sé que suena desagradable (¡lo es!), Pero no sé qué más podrías hacer aparte de vivir con la lentitud de la única consulta que tienes ahora. – HansUp

+0

@HansUp: gracias por su aporte. Temía que tendría que usar un truco tan feo, pero si no puedo encontrar otra solución, tendré que usar una. Mis usuarios esperan el resultado de esta consulta un par de veces al día y 17 segundos es mucho tiempo cuando todo lo que haces es mirar fijamente a la pantalla. –

Respuesta

2

Finalmente lo puse a trabajar mezclando cosas hasta que el planificador de consultas estuvo de acuerdo conmigo. I aislado del "A.primaryKey = 1" en un sub-consulta para asegurarse de que es ejecutado antes de que A se une a B. Es algo como esto:

SELECT ... 
FROM (SELECT ... FROM A WHERE a.primaryKey=1) AS qryA 
    INNER JOIN B ... 
WHERE D.d = 2; 
2

hacerlo en código VBA? La idea sería eliminar la parte que es lenta y ejecutar la consulta de devolución rápida, luego agregar la parte lenta en sql.

db.execute "select * from qryFast inner join d on qryfast.dkey = d.d where d.d = 2 

No, el código VBA en un módulo es diferente de una sub-consulta. @HansUp nos ha aclarado que la ejecución del código en un solo paso, como he demostrado anteriormente, no mejorará el rendimiento. Debería poder obtener los resultados rápidamente en la memoria, si está familiarizado con la escritura de código en los módulos, pero luego obtener el resultado donde lo necesita puede ralentizarlo más.


en otras palabras, usted debe ser capaz de obtener los resultados de qryFast en un conjunto de registros en la memoria de forma rápida, y luego aplicar un filtro en qryFast.dkey = d, y también obtiene rápidamente un conjunto de registros de 'select * from tableD donde d = 2' para buscar la información relacionada que desea de tableD, pero sacando todo eso de la memoria y a un lugar donde su front-end puede acceder puede llevar más tiempo que los 17 segundos que están esperando ahora.


De hecho, puede ser que una patada en los pantalones suficientes si cambia qryFast para incluir una condición en la tecla d = 2 (o cualquiera que sea el pk está en presentadas)


otra idea: tiene 3 consultas, qryFast, qryD y qryFastWithD uniendo las dos. Estoy lanzando ideas, aquí.


o, como se dice en sus comentarios, intente que contiene diferentes partes de la consulta en la sub-consultas, pero yo creo que el optimizador no se deje engañar por este truco, si se mueve un pedazo de él en una sub consulta no funcionó. Por supuesto, lo que sea que funcione, tómelo.

+0

No, no lo he intentado, supongo que el optimizador podría no extenderse a VBA. En lugar de hacerlo todo en una declaración de SQL como he mostrado, podría intentar dividirla en varios pasos en VBA. – Beth

+0

A menos que me pierda algo, es lo mismo que ya probé. Creé una subconsulta y la utilicé como fuente de otra consulta que simplemente SELECCIONA * FROM qryFast DONDE d = 2. –

+1

@Beth: todas las consultas SQL, tanto si provienen de una consulta almacenada como si se generan en VBA, aún las ejecuta el JET motor ... creo. Correcto... ? –

Cuestiones relacionadas