使用索引的誤區(qū)之五:空值的妙用
2024-07-21 02:06:29
供稿:網(wǎng)友
 
使用索引的誤區(qū)之五:空值的妙用
并不是因為完全為空的條目不被記錄到索引中,就堅決不能使用空值,相反,有時候合理使用oracle的空值會為我們的查詢帶來幾倍甚至幾十倍的效率提高。
舉個例子,加入有一個表,里面有個字段是“處理時間”,如果沒有處理的事務(wù),該列就為空,并且在大部分情況下,處理的事務(wù)總是在總記錄數(shù)的10%或者更少,而等待處理的記錄(“處理時間”這列為空)總是絕大多數(shù)的記錄,那么在“等待時間”這列上建立索引,索引中就總是會保存很少的記錄,我們希望的訪問方式是,當(dāng)訪問表中所有代處理的記錄(即10%或者更多的記錄數(shù)目)時,我們希望通過全表掃描的方式來檢索;然而,當(dāng)我們希望訪問已經(jīng)處理的事務(wù)(即5%或者更少的記錄數(shù)目)時,我們希望通過索引來訪問,因為索引中的記錄數(shù)目很少,請看下面的例子:
sql> create table tt as select * from sys.dba_objects;
 
table created
 
executed in 0.601 seconds
 
sql> alter table tt add (t int);
 
table altered
 
executed in 0.061 seconds
 
sql> select count(*) from tt;
 
  count(*)
----------
      6131c
 
executed in 0.01 seconds
 
sql> update tt set t=1 where owner='demo';
 
10 rows updated
 
executed in 0.03 seconds
 
sql> commit;
 
commit complete
 
executed in 0 seconds
 
sql> select count(*) from tt where owner='demo';
 
  count(*)
----------
        10  ――――――――――――――已經(jīng)處理的數(shù)目
 
executed in 0.08 seconds
s
sql> select count(*) from tt;
 
  count(*)
----------
      6131      ――――――――――――――總記錄數(shù)目
 
 
executed in 0.01 seconds
 
下面的查詢因為訪問表中的大多數(shù)記錄(代處理的記錄,即10%以上的記錄數(shù)目),可以看見,它如我們所希望的那樣使用了全表掃描:
 
select object_name from tt where t is null;
 
plan_table_output
--------------------------------------------------------------------------------
--------------------------------------------------------------------
| id  | operation            |  name       | rows  | bytes | cost  |
--------------------------------------------------------------------
|   0 | select statement     |             |       |       |       |
|*  1 |  table access full   | tt          |       |       |       |
--------------------------------------------------------------------
predicate information (identified by operation id):
---------------------------------------------------
   1 - filter("tt"."t" is null)
note: rule based optimization
 
14 rows selected
 
executed in 0.05 seconds
 
下面的查詢因為要訪問表中的少數(shù)記錄,我們希望通過索引來訪問:
select object_name from tt where t=1;
 
plan_table_output
--------------------------------------------------------------------------------
--------------------------------------------------------------------
| id  | operation            |  name       | rows  | bytes | cost  |
--------------------------------------------------------------------
|   0 | select statement     |             |       |       |       |
|*  1 |  table access full   | tt          |       |       |       |
--------------------------------------------------------------------
predicate information (identified by operation id):
---------------------------------------------------
   1 - filter("tt"."t"=1)
note: rule based optimization
 
14 rows selected
 
executed in 0.06 seconds
請注意,這里并沒有如我們所希望的那樣使用索引,而是使用了全表掃描,這里有一個結(jié)論:
建立了索引后,要想在cbo下合理的使用索引,一定要定期的更新統(tǒng)計信息
 
下面我們分析一下索引,看看有什么效果:
sql> analyze index tt_idx validate structure;
 
index analyzed
 
executed in 0 seconds
 
sql> select lf_rows from index_stats;
 
   lf_rows
----------
        10  ――――――――――索引中總共有10行
 
executed in 0.05 seconds
 
sql> exec dbms_stats.gather_index_stats('demo','tt_idx');
 
pl/sql procedure successfully completed
 
executed in 0.03 seconds
 
sql> select distinct_keys from user_indexes;
 
distinct_keys
-------------
            1   ――――――――――只有一個鍵值
 
executed in 0.05 seconds
 
sql> select * from tt where t is null;
 
已選擇6121行。
 
 
execution plan
----------------------------------------------------------
   0      select statement optimizer=choose
   1    0   table access (full) of 'tt'
 
 
 
 
statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
        485  consistent gets
          0  physical reads
          0  redo size
     355012  bytes sent via sql*net to client
       4991  bytes received via sql*net from client
        410  sql*net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
       6121  rows processed
 
sql> select * from tt where t=5;
 
未選定行
 
 
execution plan
----------------------------------------------------------
   0      select statement optimizer=choose
   1    0   table access (by index rowid) of 'tt'
   2    1     index (range scan) of 'tt_idx' (non-unique)
 
 
 
 
statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
          1  consistent gets
          0  physical reads
          0  redo size
        964  bytes sent via sql*net to client
        372  bytes received via sql*net from client
          1  sql*net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          0  rows processed
 
sql> select * from tt where t=1;
 
已選擇10行。
 
 
execution plan
----------------------------------------------------------
   0      select statement optimizer=choose
   1    0   table access (by index rowid) of 'tt'
   2    1     index (range scan) of 'tt_idx' (non-unique)
 
 
 
 
statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
          4  consistent gets
          0  physical reads
          0  redo size
       1639  bytes sent via sql*net to client
        503  bytes received via sql*net from client
          2  sql*net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
         10  rows processed
 
sql> update tt set t=2 where t=1;
 
已更新10行。
 
 
execution plan
----------------------------------------------------------
   0      update statement optimizer=choose
   1    0   update of 'tt'
   2    1     index (range scan) of 'tt_idx' (non-unique)
 
 
 
 
statistics
----------------------------------------------------------
          0  recursive calls
         14  db block gets
          1  consistent gets
          0  physical reads
       3216  redo size
        616  bytes sent via sql*net to client
        527  bytes received via sql*net from client
          3  sql*net roundtrips to/from client
          2  sorts (memory)
          0  sorts (disk)
         10  rows processed
 
sql> set autotrace traceonly
sql> update tt set t=3 where t is null;
 
6121 rows updated.
 
 
execution plan
----------------------------------------------------------
   0      update statement optimizer=choose
   1    0   update of 'tt'
   2    1     table access (full) of 'tt'
 
 
 
 
statistics
----------------------------------------------------------
          0  recursive calls
      18683  db block gets
         80  consistent gets
          0  physical reads
    2583556  redo size
        618  bytes sent via sql*net to client
        533  bytes received via sql*net from client
          3  sql*net roundtrips to/from client
          1  sorts (memory)
          0  sorts (disk)
       6121  rows processed
 
sql>