Oracle 何时会对空列值创建索引?

14

我曾认为当一个列值为null时,Oracle不会对一行进行索引。

通过简单的实验,证明这种情况并非如此。我能够运行一些查询,尽管某些列可为空 (这当然是一个惊喜),但仍然意外地只访问索引。

谷歌搜索得到了一些有关答案的博客:我读到一个结论,即除非所有索引列都为null,否则将索引一行;还有一个结论是,只要索引的前导列值不为null,就会索引一行。

那么,在哪些情况下不会将一行输入索引呢?这与Oracle版本有关吗?

3个回答

27

如果任何索引列包含非空值,则将对该行进行索引。如下面的示例所示,只有一行未被索引,即在两个索引列中均为NULL的行。您还可以看到,当主导索引列具有NULL值时,Oracle肯定会对该行进行索引。

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 上运行。但我非常自信它适用于所有版本。


12
除了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个一致的读取。

祝好, 罗布


3
除了APC的回答,空值也会在位图索引中进行索引。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接