2011-08-02 17 views
8

Tengo una consulta simple, que selecciona las primeras 200 filas ordenadas por una de las columnas filtradas por otra columna indexada. La confusión es la razón es que el plan de consulta en PL/SQL Developer muestra que se utiliza este índice única cuando estoy seleccionando todas las filas, por ejemplo:Índice incorrecto que se utiliza al seleccionar las filas superiores

SELECT * FROM 
(
SELECT * 
FROM cr_proposalsearch ps 
WHERE UPPER(ps.customerpostcode) like 'MK3%' 
ORDER BY ps.ProposalNumber DESC 
) 
WHERE ROWNUM <= 200 

Plan de muestra que usa el índice CR_PROPOSALSEARCH_I1, que es un índice en dos columnas: PROPOSALNUMBER & SUPERIOR (CustomerName), esto se lleva a 0.985s a ejecutar: query with ROWNUM

Si me deshago de la condición ROWNUM, el plan es lo que espero y que se ejecuta en 0.343s : query without ROWNUM

Dónde index XIF25CR_PROPOSALSEARCH is on CR_PROPOSALSEARCH (UPPER(CUSTOMERPOSTCODE));

¿Por qué?

EDITAR: Me han reunido estadísticas sobre cr_proposalsearch mesa y dos planes de consulta ahora demostrar que los usan XIF25CR_PROPOSALSEARCH índice.

+0

dos consultas diferentes, dos conjuntos de resultados diferentes, ¿por qué no debería haber dos planes diferentes explicar? – APC

Respuesta

8

La inclusión de ROWNUM modifica los cálculos del optimizador acerca de cuál es la ruta más eficiente.

Cuando hace una consulta top-n como esta, no significa necesariamente que Oracle obtendrá todas las filas, las clasificará completamente y luego devolverá las superiores. La operación COUNT STOPKEY en el plan de ejecución indica que Oracle solo realizará las operaciones subyacentes hasta que haya encontrado el número de filas que solicitó.

El optimizador ha calculado que la consulta completa adquirirá y ordenará 77K filas. Si utilizara este plan para la consulta top-n, tendría que hacer un gran tipo de esas filas para encontrar las 200 principales (no necesariamente tendría que ordenarlas completamente, ya que no le importaría el orden exacto de filas más allá de la parte superior, pero tendría que mirar todas esas filas).

El plan para la consulta top-n usa el otro índice para evitar tener que ordenarlo en absoluto. Considera cada fila en orden, comprueba si coincide con el predicado y, de ser así, la devuelve. Cuando se devuelven 200 filas, ya está hecho. Sus cálculos indican que esto será más eficiente para obtener un pequeño número de filas. (Puede que no sea correcto, por supuesto, no ha dicho cuál es el rendimiento relativo de estas consultas).)

Si el optimizador fuera a elegir este plan cuando solicite todas las filas, debería leer todo el índice en orden descendente, obteniendo cada fila de la tabla por ROWID a medida que va contra el predicado. Esto daría como resultado una gran cantidad de E/S adicionales e inspeccionaría muchas filas que no serían devueltas. Entonces, en este caso, decide que usar el índice en customerpostcode es más eficiente.

Si aumenta gradualmente el número de filas que se devolverán desde la consulta top-n, probablemente encontrará un punto de inflexión donde el plan cambia de la primera a la segunda. Solo por los costos de los dos planes, supongo que esto podría ser alrededor de 1,200 filas.

+0

genio, el umbral es 1166, ¿cómo lo sabías? He agregado tiempos y detalles de otro índice. Si le dices a la consulta que use el índice según la respuesta de @Kevin Burton, el tiempo siempre es alrededor de 0.3s – Tsar

+0

pls note, el recuento Rownum máximo para mí siempre será 200. ¿Esto significa que nunca se usará XIF25CR_PROPOSALSEARCH? ¿Hay alguna otra opción de optimización que pueda usar aquí? – Tsar

+0

Supuse que el costo del primer plan aumentaría linealmente. 6 x 951 = 5,706, muy cerca del costo del segundo plan de 5,512. Así que calculé que el punto de inflexión sería cercano a 6 x 200 = 1200. –

1

Parece que no tiene un índice perfectamente ajustado. El índice CR_PROPOSALSEARCH_I1 se puede usar para recuperar las filas en orden descendente del atributo PROPOSALNUMBER. Probablemente se elija porque Oracle puede evitar recuperar todas las filas coincidentes, ordenarlas de acuerdo con la cláusula ORDER BY y luego descartar todas las filas, excepto las primeras.

Sin la condición ROWNUM, Oracle utiliza el índice XIF25CR_PROPOSALSEARCH (usted no dio ningún detalle acerca de él) porque es probablemente bastante selectivo con respecto a la cláusula WHERE. Pero requerirá ordenar el resultado después. Este es probablemente el plan más eficiente basado en la suposición de que recuperará todas las filas.

Dado que cualquiera de los índices es un trade-off (uno es mejor para la clasificación, el otro mejor para aplicar la cláusula WHERE), detalles como ROWNUM determinan qué plan de ejecución elige Oracle.

4

Si está seguro de sus estadísticas son hasta la fecha y que el índice es bastante selectiva, se notaba oráculo para utilizar el índice

SELECT * 
FROM (SELECT /*+ index(ps XIF25CR_PROPOSALSEARCH) */ * 
     FROM  cr_proposalsearch ps 
     WHERE UPPER (ps.customerpostcode) LIKE 'MK3%' 
     ORDER BY ps.proposalnumber DESC) 
WHERE ROWNUM <= 200 

(sólo recomendaría este enfoque como último recurso)

Si estuviera haciendo esto me primera TKPROF la consulta para ver realmente la cantidad de trabajo que está haciendo,

por ejemplo: el costo de escaneo de rangos índice podría estar muy lejos

olvidó mencionar .... Usted debe verificar la cardinalidad real:

SELECT count(*) FROM cr_proposalsearch ps WHERE UPPER(ps.customerpostcode) like 'MK3%' 

y luego compararla con la cardinalidad en el plan de consulta.

1

Esta condición:

WHERE UPPER(ps.customerpostcode) like 'MK3%' 

no es continua, es decir que no se puede mantener una única gama ordenada por ello.

Así que hay dos formas de ejecutar esta consulta:

  1. orden de su número y luego filtrar por código.
  2. Filtrar en el código luego ordenar por número.

Método 1 usuario es capaz de un índice en el número que le da tiempo de ejecución lineal (top 100 filas serían seleccionados 2 veces más rápido que la parte superior 200, siempre que el número y el código no se correlacionan).

Método 2 es capaz de utilizar un rango de exploración para la filtración gruesa en el código (la condición rango sería algo así como code >= 'MK3' AND code < 'MK4'), sin embargo, se requiere de una especie ya que el orden de número no puede ser conservado en un índice compuesto.

El tiempo de ordenación depende del número de filas superiores que también esté seleccionando, pero esta dependencia, a diferencia del método 1, no es lineal (siempre necesita al menos una exploración de rango).

Sin embargo, la condición de filtrado en el método 2 es lo suficientemente selectiva para el RANGE SCAN con un tipo posterior para ser más eficiente que FULL SCAN para toda la tabla.

Esto significa que hay un punto de inflexión: para esta condición: ROWNUM <= X existe un valor de X por lo que el método 2 se vuelve más eficiente cuando se excede este valor.

Actualización:

Si siempre está buscando por lo menos en 3 primeros símbolos, puede crear un índice de esta manera:

SUBSTRING(UPPER(customerpostcode), 1, 3), proposalnumber 

y utilizarlo en esta consulta:

SELECT * 
FROM (
     SELECT * 
     FROM cr_proposalsearch ps 
     WHERE SUBSTRING(UPPER(customerpostcode, 1, 3)) = SUBSTRING(UPPER(:searchquery), 1, 3) 
       AND UPPER(ps.customerpostcode) LIKE UPPER(:searchquery) || '%' 
     ORDER BY 
       proposalNumber DESC 
     ) 
WHERE rownum <= 200 

de esta manera, el orden de los números serán preservados por separado para cada conjunto de códigos compartiendo primera 3 letras que le darán un escaneo de índice más denso.

Cuestiones relacionadas