2011-09-24 22 views
7

Mi pregunta se refiere a Oracle 11g y al uso de índices en consultas SQL.Oracle 11g: Índice no utilizado en "select distinct" -query

En mi base de datos, no es una tabla que está estructurado como sigue:

Table tab (
    rowid NUMBER(11), 
    unique_id_string VARCHAR2(2000), 
    year NUMBER(4), 
    dynamic_col_1 NUMBER(11), 
    dynamic_col_1_text NVARCHAR2(2000) 
) TABLESPACE tabspace_data; 

He creado dos índices:

CREATE INDEX Index_dyn_col1 ON tab (dynamic_col_1, dynamic_col_1_text) TABLESPACE tabspace_index; 
CREATE INDEX Index_unique_id_year ON tab (unique_id_string, year) TABLESPACE tabspace_index; 

La tabla contiene alrededor de 1 a 2 millones de registros. Puedo extraer los datos de la misma mediante la ejecución del siguiente comando SQL:

SELECT distinct 
"sub_select"."dynamic_col_1" "AS_dynamic_col_1","sub_select"."dynamic_col_1_text" "AS_dynamic_col_1_text" 
FROM 
(
    SELECT "tab".* FROM "tab" 
    where "tab".year = 2011 
) "sub_select" 

Por desgracia, la consulta necesita alrededor de 1 hora para ejecutar, a pesar de que he creado los índices de ambas descritas anteriormente. El plan de explicación muestra que Oracle utiliza un "Acceso completo a la tabla", es decir, una exploración de tabla completa. ¿Por qué no se usa el índice?

Como un experimento, he probado el siguiente comando SQL:

SELECT DISTINCT 
"dynamic_col_1" "AS_dynamic_col_1", "dynamic_col_1_text" "AS_dynamic_col_1_text" 
FROM "tab" 

Incluso en este caso, el índice no se utiliza y se realiza un escaneo completo de tabla.

En mi base de datos real, la tabla contiene más columnas indexadas como "dynamic_col_1" y "dynamic_col_1_text". El archivo de índice completo tiene un tamaño de aproximadamente 50 GB.

Un poco más de información:

  • La base de datos es Oracle 11g instalado en el ordenador local.
  • Uso Windows 7 Enterprise 64bit.
  • El índice completo está dividido en 3 archivos dbf con un tamaño de aproximadamente 50 GB.

Realmente me alegraría si alguien pudiera decirme cómo hacer que Oracle use el índice en la primera consulta. Como la primera consulta es utilizada por otro programa para extraer los datos de la base de datos, apenas se puede cambiar. Entonces, sería bueno modificar la mesa.

Gracias de antemano.

[01.10.2011: UPDATE]

creo que he encontrado la solución para el problema. Ambas columnas dynamic_col_1 y dynamic_col_1_text son nulables. Después de alterar la tabla para prohibir los valores "NULOS" en ambas columnas y agregar un nuevo índice únicamente para la columna year, Oracle realiza una Exploración rápida de índice. La ventaja es que la consulta tarda unos 5 segundos en ejecutarse y no 1 hora como antes.

+0

'rowid' campo es la clave principal? –

+0

Por favor, no llame a una columna en una tabla rowid, usted está pidiendo un mundo de dolor si alguna vez necesita usar el [row-pseudo-column] actual (http://download.oracle.com/docs/cd /B19306_01/server.102/b14200/pseudocolumns008.htm) – Ben

+0

@BogdanSahlean Sí, rowid es la clave principal –

Respuesta

1

No sé si es relevante, pero he probado la siguiente consulta:

SELECT DISTINCT 
"dynamic_col_1" "AS_dynamic_col_1", "dynamic_col_1_text" "AS_dynamic_col_1_text" 
FROM "tab" 
WHERE "dynamic_col_1" = 123 AND "dynamic_col_1_text" = 'abc' 

El plan para esa búsqueda muestran que Oracle utiliza un recorrido de índice en este escenario a explicar.

Las columnas dynamic_col_1 y dynamic_col_1_text son nulables. ¿Esto tiene un efecto en el uso del índice?

01.10.2011: UPDATE]

Creo que he encontrado la solución para el problema. Ambas columnas dynamic_col_1 y dynamic_col_1_text son anulables. Después de alterar la tabla para prohibir los valores "NULOS" en ambas columnas y agregar un nuevo índice únicamente para el año de columna, Oracle realiza una Exploración de índice rápido. La ventaja es que la consulta tarda unos 5 segundos en ejecutarse y no 1 hora como antes.

2

No tengo una instancia de Oracle a la mano, así que esto es un poco complicado, pero mi inclinación es decir que es porque tiene el índice compuesto en el orden incorrecto. Si tenía year como la primera columna en el índice, podría usarla.

4

Su índice debe ser:

CREATE INDEX Index_year 
ON tab (year) 
TABLESPACE tabspace_index; 

Además, la consulta podría ser simplemente:

SELECT DISTINCT 
     dynamic_col_1 "AS_dynamic_col_1", 
     dynamic_col_1_text "AS_dynamic_col_1_text" 
    FROM tab 
WHERE year = 2011; 

Si su índice fue creado exclusivamente para esta consulta sin embargo, usted podría crearlo incluyendo los dos descabellada también columnas, entonces el optimizador no tendría que ir a la tabla para los datos de la consulta, podría recuperarlo directamente del índice haciendo que su consulta sea más eficiente nuevamente.

creo que sirve ...

+1

Suponiendo que @jonearles es incorrecto y el índice ayudaría y él usa su consulta (lo que yo haría). El mejor índice sería 'year, dynamic_col_1, dynamic_col_1_text' ya que no necesitaría volver a acceder a la tabla por rowid (el rowid real) después. Él podría simplemente tomar todos los datos del índice. – Ben

+0

@Ollie Ya pensé en eso. Pero si elimino la columna 'year' completamente de la consulta y ejecuto' SELECT DISTINCT "dynamic_col_1" "AS_dynamic_col_1", "dynamic_col_1_text" "AS_dynamic_col_1_text" FROM "tab" ', el índice no se usa, también. –

+0

@Ben El ejemplo anterior solo debe ilustrar el problema. En la base de datos real, hay más de 200 hundret de esas columnas 'dynamic_col_1' y' dynamic_col_1_text' que deben ser indexadas. ¡Supongo que usar un GRAN índice de más de 500 columnas no es una buena idea! –

5

¿Seguro que un índice de acceso sería más rápido que una mesa de exploración completa? Como estimación aproximada, los escaneos completos de tablas son 20 veces más rápidos que leer un índice. Si tab tiene más del 5% de los datos en 2011, no es sorprendente que Oracle use un escaneo de tabla completo. Y como @Dan y @Ollie mencionaron, con year como la segunda columna, esto hará que el índice sea aún más lento.

Si el índice es realmente más rápido, entonces el problema probablemente sea una mala estadística. Hay cientos de formas en que las estadísticas podrían ser malas. Muy brevemente, esto es lo que miraría primero:

  1. Ejecutar un plan de explicación con y sin y una sugerencia de índice. ¿Las cardinalidades están desactivadas 10 veces o más? ¿Los tiempos están desactivados en 10x o más?
  2. Si la cardinalidad está desactivada, asegúrese de que haya estadísticas actualizadas en la tabla y el índice, y esté utilizando un ESTIMATE_PERCENT razonable (DBMS_STATS.AUTO_SAMPLE_SIZE es casi siempre el mejor para 11g).
  3. Si la hora está apagada, verifique las estadísticas de su carga de trabajo.
  4. ¿Está utilizando el paralelismo? Oracle siempre asume una mejora casi lineal para el paralelismo, pero en un escritorio con un disco duro probablemente no verá ninguna mejora en absoluto.

Además, esto no es realmente relevante para su problema, pero es posible que desee evitar el uso de identificadores entre comillas. Una vez que los usa, debe usarlos en todas partes, y generalmente hace que sus tablas y consultas sean dolorosas para trabajar.

+0

+1, las estadísticas desactualizadas son la causa de muchos problemas de rendimiento. Buena consulta sobre el porcentaje de registros a los que el OP va a acceder también. Un FTS en realidad podría ser el método de acceso más eficaz si está recuperando un porcentaje razonable de los registros de la tabla. – Ollie

+0

@Ollie Bueno, corrígeme si me equivoco, pero pensé que al usar un índice en ambas columnas "dynamic_col_1 y dynamic_col_1_text aceleraría la consulta" select distinct dynamic_col_1, dynamic_col_1_text ... ". Desafortunadamente, apenas puedo cambiar el sql query porque es generado por otra herramienta para extraer los datos de la tabla. Por lo tanto, esos identificadores entre comillas son inevitables. –

+0

@ oracle_user54 El punto que Ollie y jonearles están llegando es que los índices no siempre son más rápidos que un FTS.Si el porcentaje de filas que va a extraer es suficientemente grande, FTS es más rápido porque (más o menos) la sobrecarga de ir y venir del índice es mayor que la ganancia de velocidad de no escanear el resto de las filas. – Dan

0

Prueba esto:

1) Crear un índice en campo de año (ver Ollie respuesta).

2) y luego utilizar esta consulta:

SELECT DISTINCT 
dynamic_col_1 
,dynamic_col_1_text 
FROM tab 
WHERE ID (SELECT ID FROM tab WHERE year=2011) 

o

SELECT DISTINCT 
dynamic_col_1 
,dynamic_col_1_text 
FROM tab 
WHERE ID (SELECT ID FROM tab WHERE year=2011) 
GROUP BY dynamic_col_1, dynamic_col_1_text 

Tal vez le ayudará.

1

Su segunda consulta de prueba:

SELECT DISTINCT 
"dynamic_col_1" "AS_dynamic_col_1", "dynamic_col_1_text" "AS_dynamic_col_1_text" 
FROM "tab" 

no usaría el índice debido a que no tienen cláusula WHERE, por lo que está pidiendo Oracle para leer cada fila de la tabla. En esa situación, la exploración de tabla completa es el método de acceso más rápido.

Además, como han mencionado otros carteles, su índice en AÑO lo tiene en la segunda columna. Oracle puede utilizar este índice realizando un análisis de omisión, pero se produce un impacto en el rendimiento al hacerlo, y dependiendo del tamaño de la tabla, Oracle puede decidir usar el FTS nuevamente.