2009-08-05 2 views
10

필자는 오라클이 열 값 중 하나가 null 일 때 행을 색인화하지 않는다고 생각했습니다.Oracle은 언제 null 열 값을 색인합니까?

몇 가지 간단한 실험을 통해 이것이 사실이 아님을 알 수 있습니다. 몇 가지 쿼리가 예기치 않게 인덱스 만 액세스 할 수 있었지만 일부 열은 nullable 이었지만 (물론 즐거운 놀라움이었습니다.)

구글 검색은 충돌 답변 일부 블로그에지도 : 나는 모든 인덱스 컬럼이 널 (null)하지 않는 한 행이 인덱스됩니다, 또한 하지 않는 인덱스의 선두 컬럼 값이 행이 색인 된 것을 읽었습니다 없는.

그래서 어떤 경우 행이 색인을 입력하지 않습니까? 이 오라클 버전은 구체적입니까?

답변

22

인덱스 된 열에 인덱스가 지정되는 null이 아닌 값이 포함되어있는 경우. 다음 예제에서 볼 수 있듯이 한 행만 인덱싱되지 않고 인덱스 된 두 열 모두에 NULL이있는 행입니다. 주요 인덱스 열에 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> 

이 예제는 Oracle 11.1.0.6에서 실행됩니다. 그러나 나는 모든 버전에 적용될 수 있다고 확신합니다.

3

APC의 답변 외에도 NULLS는 비트 맵 색인으로 색인됩니다.

8

그리고 APC의 대답 외에도 NULL 값을 색인하려는 경우 상수 표현식을 색인에 추가 할 수 있습니다.

예 :

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 

전체 테이블을 스캔하고 일관성이 얻는 364을 유의하시기 바랍니다.

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 

이제는 색인을 사용하며 3 개의 일치하는 가져 오기 만 있습니다.

감사합니다. Rob.