2008-10-07 20 views
5

Digamos que tiene una tabla en Oracle:¿Cómo usar un índice basado en funciones en una columna que contiene NULLs en Oracle 10+?

CREATE TABLE person (
    id NUMBER PRIMARY KEY, 
    given_names VARCHAR2(50), 
    surname VARCHAR2(50) 
); 

con estos índices basados ​​en funciones:

CREATE INDEX idx_person_upper_given_names ON person (UPPER(given_names)); 
CREATE INDEX idx_person_upper_last_name ON person (UPPER(last_name)); 

Ahora, given_names no tiene valores NULL pero para last_name motivo de la discusión hace. Si hago esto:

SELECT * FROM person WHERE UPPER(given_names) LIKE 'P%' 

al explicar el plan me dice su utilizando el índice, pero cambiarlo a:

SELECT * FROM person WHERE UPPER(last_name) LIKE 'P%' 

no lo hace. Los documentos de Oracle dicen que usar el índice basado en funciones solo se usará cuando se cumplan varias condiciones, una de las cuales es garantizar que no haya valores NULOS ya que no están indexadas.

que he probado estas consultas:

SELECT * FROM person WHERE UPPER(last_name) LIKE 'P%' AND UPPER(last_name) IS NOT NULL 

y

SELECT * FROM person WHERE UPPER(last_name) LIKE 'P%' AND last_name IS NOT NULL 

En este último caso, incluso he añadido un índice en last_name pero no importa lo que intento que utiliza un escaneo completo de tabla. Suponiendo que no puedo deshacerme de los valores NULL, ¿cómo obtengo esta consulta para usar el índice en UPPER (last_name)?

+0

¿Cuántas filas realmente tiene en la tabla? ¿Puede publicar el plan de explicación para el escaneo completo de la tabla y también cuando elige usar el índice (es posible que deba insinuarlo o cambiar la columna a NOT NULL para los fines del ejercicio). –

Respuesta

7

El índice puede ser utilizado, aunque el optimizador puede haber optado por no utilizarlo para su ejemplo particular:

SQL> create table my_objects 
    2 as select object_id, object_name 
    3 from all_objects; 

Table created. 

SQL> select count(*) from my_objects; 
    2/

    COUNT(*) 
---------- 
    83783 


SQL> alter table my_objects modify object_name null; 

Table altered. 

SQL> update my_objects 
    2 set object_name=null 
    3 where object_name like 'T%'; 

1305 rows updated. 

SQL> create index my_objects_name on my_objects (lower(object_name)); 

Index created. 

SQL> set autotrace traceonly 

SQL> select * from my_objects 
    2 where lower(object_name) like 'emp%'; 

29 rows selected. 


Execution Plan 
---------------------------------------------------------- 

------------------------------------------------------------------------------------ 
| Id | Operation     | Name   | Rows | Bytes | Cost (%CPU)| 
------------------------------------------------------------------------------------ 
| 0 | SELECT STATEMENT   |     | 17 | 510 | 355 (1)| 
| 1 | TABLE ACCESS BY INDEX ROWID| MY_OBJECTS  | 17 | 510 | 355 (1)| 
|* 2 | INDEX RANGE SCAN   | MY_OBJECTS_NAME | 671 |  |  6 (0)| 
------------------------------------------------------------------------------------ 

La documentación que leí fue presumiblemente señalando que, al igual que cualquier otro índice, todo- las claves nulas no se almacenan en el índice.

2

En su ejemplo, ha creado el mismo índice dos veces; esto daría un error, por lo que supongo que fue un error al pegar, no el código real que probó.

he probado con

CREATE INDEX idx_person_upper_surname ON person (UPPER(surname)); 

SELECT * FROM person WHERE UPPER(surname) LIKE 'P%'; 

y produjo el plan de consulta esperada:

Execution Plan 
---------------------------------------------------------- 
    0  SELECT STATEMENT Optimizer=ALL_ROWS (Cost=1 Card=1 Bytes=67) 
    1 0 TABLE ACCESS (BY INDEX ROWID) OF 'PERSON' (TABLE) (Cost=1 
      Card=1 Bytes=67) 

    2 1  INDEX (RANGE SCAN) OF 'IDX_PERSON_UPPER_SURNAME' (INDEX) 
      (Cost=1 Card=1) 

Para responder a su pregunta, sí que debería funcionar. Intente verificar dos veces si tiene el segundo índice creado correctamente.

También intente una sugerencia explícita:

SELECT /*+INDEX(PERSON IDX_PERSON_UPPER_SURNAME)*/ * 
FROM person 
WHERE UPPER(surname) LIKE 'P%'; 

Si eso funciona, pero sólo con la pista, entonces está probablemente relacionado con las estadísticas CBO ido mal, o parámetros relacionados init CBO.

+0

¿Pusiste NULL en la tabla y obtuviste ese plan de consulta? – cletus

0

¿Estás seguro de que deseas que se use el índice? Los escaneos completos de tabla no son malos. Dependiendo del tamaño de la tabla, puede ser más eficiente hacer un escaneo de tabla que usar un índice. También depende de la densidad y la distribución de los datos, razón por la cual se recopilan las estadísticas. El optimizador basado en el costo generalmente se puede confiar para tomar la decisión correcta. A menos que tengas un problema de rendimiento específico, no me preocuparía demasiado por eso.

0

Puede eludir el problema de los valores nulos se unindexed en esta u otras situaciones por También la indexación basada en un valor literal:

CREATE INDEX idx_person_upper_surname ON person (UPPER(surname),0); 

Esto le permite utilizar el índice para este tipo de consultas como:

Select * 
From person 
Where UPPER(surname) is null; 

Esta consulta normalmente no utiliza un índice, excepto los índices de mapa de bits o índices que incluyen una columna real no anulable que no sea el apellido.

+0

David, ¿qué tipo de consulta podría hacer uso de dicho índice? –

+1

Cualquier consulta que utilizó UPPER (apellido). El truco de David asegura que los valores nulos de UPPER (apellido) estén indexados. Oracle no indexa si todos los valores son nulos. El valor literal de 0 garantiza que esto nunca ocurra. –

0

Oracle seguirá utilizando un índice basado en funciones con columnas que contengan nulo: creo que malinterpretaste la documentación.

Debe poner un nvl en el índice de función si desea comprobar esto.

Algo así como ...

create index idx_person_upper_surname on person (nvl(upper(surname),'N/A')); 

A continuación, puede consulta utilizando el índice con

select * from person where nvl(upper(surname),'N/A') = 'PIERPOINT' 

Aunque, todo un poco feo. Como la mayoría de las personas tiene apellidos, tal vez sea apropiado un "no nulo" :-).

Cuestiones relacionadas