2009-08-05 29 views
10

Solía ​​pensar que Oracle no indexa una fila cuando uno de los valores de la columna es nulo.¿Cuándo Oracle indexa valores nulos de columna?

Algunos experimentos simples muestran que este no es el caso. Pude ejecutar algunas consultas de manera inesperada, solo accediendo a índices, aunque algunas columnas eran anulables (lo cual, por supuesto, fue una agradable sorpresa).

Una búsqueda en Google llevó a algunos blogs con respuestas contradictorias: He leído que una fila se indexan a menos todos columnas indizadas son nulos, y también que una fila se indexan a menos el valor de columna que lleva para el índice es nulo.

Entonces, ¿en qué casos una fila no ingresa un índice? ¿Es esta versión de Oracle específica?

Respuesta

22

Si cualquier columna indexada contiene un valor no nulo, esa fila se indexará. Como puede ver en el siguiente ejemplo, solo una fila no se indexa y esa es la fila que tiene NULL en ambas columnas indexadas. También puede ver que Oracle indexa definitivamente una fila cuando la columna de índice principal tiene un valor NULL.

SQL> create table big_table as 
    2  select object_id as pk_col 
    3    , object_name as col_1 
    4    , object_name as col_2 
    5 from all_objects 
    6/

Table created. 

SQL> select count(*) from big_table 
    2/

    COUNT(*) 
---------- 
    69238 

SQL> insert into big_table values (9999990, null, null) 
    2/

1 row created. 

SQL> insert into big_table values (9999991, 'NEW COL 1', null) 
    2/

1 row created. 

SQL> insert into big_table values (9999992, null, 'NEW COL 2') 
    2/

1 row created. 

SQL> select count(*) from big_table 
    2/

    COUNT(*) 
---------- 
    69241 

SQL> create index big_i on big_table(col_1, col_2) 
    2/

Index created. 

SQL> exec dbms_stats.gather_table_stats(user, 'BIG_TABLE', cascade=>TRUE) 

PL/SQL procedure successfully completed. 


SQL> select num_rows from user_indexes where index_name = 'BIG_I' 
    2/

    NUM_ROWS 
---------- 
    69240 

SQL> set autotrace traceonly exp 
SQL> 
SQL> select pk_col from big_table 
    2 where col_1 = 'NEW COL 1' 
    3/

Execution Plan 
---------------------------------------------------------- 
Plan hash value: 1387873879 

----------------------------------------------------------------------------------------- 
| Id | Operation     | Name  | Rows | Bytes | Cost (%CPU)| Time  | 
----------------------------------------------------------------------------------------- 
| 0 | SELECT STATEMENT   |   |  2 | 60 |  4 (0)| 00:00:01 | 
| 1 | TABLE ACCESS BY INDEX ROWID| BIG_TABLE |  2 | 60 |  4 (0)| 00:00:01 | 
|* 2 | INDEX RANGE SCAN   | BIG_I  |  2 |  |  3 (0)| 00:00:01 | 
----------------------------------------------------------------------------------------- 

Predicate Information (identified by operation id): 
--------------------------------------------------- 

    2 - access("COL_1"='NEW COL 1') 

SQL> select pk_col from big_table 
    2 where col_2 = 'NEW COL 2' 
    3/

Execution Plan 
---------------------------------------------------------- 
Plan hash value: 3993303771 

------------------------------------------------------------------------------- 
| Id | Operation   | Name  | Rows | Bytes | Cost (%CPU)| Time  | 
------------------------------------------------------------------------------- 
| 0 | SELECT STATEMENT |   |  2 | 60 | 176 (1)| 00:00:03 | 
|* 1 | TABLE ACCESS FULL| BIG_TABLE |  2 | 60 | 176 (1)| 00:00:03 | 
------------------------------------------------------------------------------- 

Predicate Information (identified by operation id): 
--------------------------------------------------- 

    1 - filter("COL_2"='NEW COL 2') 

SQL> select pk_col from big_table 
    2 where col_1 is null 
    3 and col_2 = 'NEW COL 2' 
    4/

Execution Plan 
---------------------------------------------------------- 
Plan hash value: 1387873879 

----------------------------------------------------------------------------------------- 
| Id | Operation     | Name  | Rows | Bytes | Cost (%CPU)| Time  | 
----------------------------------------------------------------------------------------- 
| 0 | SELECT STATEMENT   |   |  1 | 53 |  4 (0)| 00:00:01 | 
| 1 | TABLE ACCESS BY INDEX ROWID| BIG_TABLE |  1 | 53 |  4 (0)| 00:00:01 | 
|* 2 | INDEX RANGE SCAN   | BIG_I  |  2 |  |  3 (0)| 00:00:01 | 
----------------------------------------------------------------------------------------- 

Predicate Information (identified by operation id): 
--------------------------------------------------- 

    2 - access("COL_1" IS NULL AND "COL_2"='NEW COL 2') 
     filter("COL_2"='NEW COL 2') 

SQL> select pk_col from big_table 
    2 where col_1 is null 
    3 and col_2 is null 
    4/

Execution Plan 
---------------------------------------------------------- 
Plan hash value: 3993303771 

------------------------------------------------------------------------------- 
| Id | Operation   | Name  | Rows | Bytes | Cost (%CPU)| Time  | 
------------------------------------------------------------------------------- 
| 0 | SELECT STATEMENT |   |  1 | 53 | 176 (1)| 00:00:03 | 
|* 1 | TABLE ACCESS FULL| BIG_TABLE |  1 | 53 | 176 (1)| 00:00:03 | 
------------------------------------------------------------------------------- 

Predicate Information (identified by operation id): 
--------------------------------------------------- 

    1 - filter("COL_1" IS NULL AND "COL_2" IS NULL) 

SQL> 

Este ejemplo se ejecuta en Oracle 11.1.0.6. Pero estoy bastante seguro de que es cierto para todas las versiones.

3

Además de la respuesta de APC, los NULLS se indexan en índices de mapa de bits.

8

Y además de la respuesta de APC: cuando desee indexar un valor NULL, puede agregar una expresión constante al índice.

Ejemplo:

SQL> select * from v$version where rownum = 1 
    2/

BANNER 
---------------------------------------------------------------- 
Oracle Database 10g Enterprise Edition Release 10.2.0.4.0 - 64bi 

1 rij is geselecteerd. 

SQL> create table t (id,status,fill) 
    2 as 
    3 select level 
    4  , nullif(ceil((level-1)/1000),0) 
    5  , lpad('*',1000,'*') 
    6  from dual 
    7 connect by level <= 10000 
    8/

Tabel is aangemaakt. 

SQL> select status 
    2  , count(*) 
    3 from t 
    4 group by status 
    5/

    STATUS COUNT(*) 
---------- ---------- 
     1  1000 
     2  1000 
     3  1000 
     4  1000 
     5  1000 
     6  1000 
     7  1000 
     8  1000 
     9  1000 
     10  999 
        1 

11 rijen zijn geselecteerd. 

SQL> create index i_status on t(status) 
    2/

Index is aangemaakt. 

SQL> exec dbms_stats.gather_table_stats(user,'t',cascade=>true) 

PL/SQL-procedure is geslaagd. 

SQL> set autotrace traceonly 
SQL> select * 
    2 from t 
    3 where status is null 
    4/

1 rij is geselecteerd. 


Uitvoeringspan 
---------------------------------------------------------- 
    0  SELECT STATEMENT Optimizer=ALL_ROWS (Cost=201 Card=1 Bytes=1007) 
    1 0 TABLE ACCESS (FULL) OF 'T' (TABLE) (Cost=201 Card=1 Bytes=1007) 


Statistics 
---------------------------------------------------------- 
      1 recursive calls 
      0 db block gets 
     364 consistent gets 
      0 physical reads 
      0 redo size 
     1265 bytes sent via SQL*Net to client 
     242 bytes received via SQL*Net from client 
      2 SQL*Net roundtrips to/from client 
      0 sorts (memory) 
      0 sorts (disk) 
      1 rows processed 

Tenga en cuenta el escaneo completo de tabla y el 364 obtiene consistente.

SQL> set autotrace off 
SQL> create index i_status2 on t(status,1) 
    2/

Index is aangemaakt. 

SQL> set autotrace traceonly 
SQL> select * 
    2 from t 
    3 where status is null 
    4/

1 rij is geselecteerd. 


Uitvoeringspan 
---------------------------------------------------------- 
    0  SELECT STATEMENT Optimizer=ALL_ROWS (Cost=1 Card=1 Bytes=1007) 
    1 0 TABLE ACCESS (BY INDEX ROWID) OF 'T' (TABLE) (Cost=1 Card=1 Bytes=1007) 
    2 1  INDEX (RANGE SCAN) OF 'I_STATUS2' (INDEX) (Cost=1 Card=1) 


Statistics 
---------------------------------------------------------- 
      1 recursive calls 
      0 db block gets 
      3 consistent gets 
      1 physical reads 
      0 redo size 
     1265 bytes sent via SQL*Net to client 
     242 bytes received via SQL*Net from client 
      2 SQL*Net roundtrips to/from client 
      0 sorts (memory) 
      0 sorts (disk) 
      1 rows processed 

Y ahora usa el índice y tiene solo 3 constantes obtiene.

Saludos, Rob.