2009-08-05 7 views
10

J'avais l'habitude de penser qu'Oracle n'indexe pas une ligne quand une des valeurs de colonne est nulle.Quand Oracle indexe-t-il des valeurs de colonne nulles?

Une simple expérimentation montre que ce n'est pas le cas. J'ai été capable d'exécuter certaines requêtes de manière inattendue en accédant uniquement à des index même si certaines colonnes étaient nullables (ce qui était bien sûr une agréable surprise).

Une recherche Google a conduit à des blogs avec des réponses contradictoires: J'ai lu qu'une ligne est indexé à moins toutes colonnes indexées sont nuls, et aussi qu'une ligne est indexé à moins la valeur de la colonne principale de l'indice est nul. Alors, dans quels cas une rangée n'entrera-t-elle pas dans un index? Cette version d'Oracle est-elle spécifique?

Répondre

22

Si une colonne indexée contient une valeur non nulle, cette ligne sera indexée. Comme vous pouvez le voir dans l'exemple suivant, une seule ligne n'est pas indexée et c'est la ligne qui a la valeur NULL dans les deux colonnes indexées. Vous pouvez également voir qu'Oracle indexe définitivement une ligne lorsque la colonne d'index principal a une valeur 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> 

Cet exemple s'exécute sur Oracle 11.1.0.6. Mais je suis assez confiant que cela est vrai pour toutes les versions.

3

En plus de la réponse d'APC, les NULLS sont indexées dans des index bitmap.

8

Et en plus de la réponse d'APC: lorsque vous voulez indexer une valeur NULL, vous pouvez ajouter une expression constante à l'index.

Exemple:

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 

S'il vous plaît noter l'analyse complète de la table et la 364 obtient cohérente.

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 

Et maintenant, il utilise l'index et a seulement 3 obtient cohérente.

Cordialement, Rob.

Questions connexes