not in 和 <> 不走索引


首先我們要知道的一點就是CBO的代碼oracle是不會對我們公開的,起碼現在是。所以本文中的結論不一定適用所有的版本。在應用本文的結論之前最好先試一下。

 

ok 下面就是本文的結論,當你在where語句中使用不等於或者not in時候,oracle 傾向於忽略索引。 比如:

SQL> Select * from test where text<>'star';

        ID TEXT
---------- ------------
   4939426 sun

這條語句即使在test上有索引,oracle也仍然會忽略。

 

 

接下來我們分析證明一下這是為什么。 其實,我認為oracle這么做是有道理的。一般我們在寫SQL的時候,如果用了 <>,也就是不等於,通常都是說選取結果集中的很大一部分。我們可以感受一下平時我們的思維方式和和習慣確實是這樣的。比如我們說要"找出這些人中不是姓李的","找出這些車中不是大眾的"。這一般來說是要返回結果集中很大一部分的。Oracle認為如果是這樣,那么用索引不如全表掃描迅速,所以這種情況根本就不考慮索引,直接采用全表掃描。 而且oracle認為,如果你知道你的<>會返回少量的結果,那么你應該會調整你的SQL 用 (< or >)來代替。

下面我們驗證一下。

首先創建表。一個很大的表。

SQL> select * from v$version;

BANNER
--------------------------------------------------------------------------------
Oracle Database 10g Enterprise Edition Release 10.2.0.5.0 - 64bi
PL/SQL Release 10.2.0.5.0 - Production
CORE    10.2.0.5.0      Production
TNS for Solaris: Version 10.2.0.5.0 - Production
NLSRTL Version 10.2.0.5.0 - Production

SQL> create table test as select rownum id, 'star' text from dba_objects,v$session;

Table created.

驗證一下這個表是不是很大,然后插入一條數據。

SQL> select count(*) from test;

  COUNT(*)
----------
   4939425

SQL> insert into test values (4939426,'sun');

1 row created.

SQL> commit;

Commit complete.

創建索引,並收集統計信息。

SQL> create index test_i on test(text);

Index created.

SQL> exec dbms_stats.gather_table_stats(ownname=> 'SYS', tabname=> 'TEST' ,cascade=> true);

PL/SQL procedure successfully completed.

進行10053trace

SQL> alter session set tracefile_identifier='haha';

Session altered.

SQL> ALTER SESSION SET EVENTS='10053 trace name context forever, level 1';

Session altered.

SQL> Select * from test where text<>'star';

        ID TEXT
---------- ------------
   4939426 sun

SQL> ALTER SESSION SET EVENTS '10053 trace name context off';

Session altered.

 

現在我們看一下10053的結果。

 

***************************************
BASE STATISTICAL INFORMATION
***********************
Table Stats::
  Table: TEST  Alias: TEST
    #Rows: 4944655  #Blks:  10780  AvgRowLen:  10.00
Index Stats::
  Index: TEST_I  Col#: 2
    LVLS: 2  #LB: 10468  #DK: 1  LB/K: 10468.00  DB/K: 19790.00  CLUF: 19790.00
***************************************
SINGLE TABLE ACCESS PATH
  -----------------------------------------
  BEGIN Single Table Cardinality Estimation
  -----------------------------------------
  Column (#2): TEXT(CHARACTER)
    AvgLen: 5.00 NDV: 1 Nulls: 0 Density: 1
  Table: TEST  Alias: TEST
    Card: Original: 4944655  Rounded: 1  Computed: 1.00  Non Adjusted: 1.00
  -----------------------------------------
  END   Single Table Cardinality Estimation
  -----------------------------------------
  Access Path: TableScan
    Cost:  3098.02  Resp: 3098.02  Degree: 0
      Cost_io: 2360.00  Cost_cpu: 2153524223
      Resp_io: 2360.00  Resp_cpu: 2153524223
  Best:: AccessPath: TableScan
         Cost: 3098.02  Degree: 1  Resp: 3098.02  Card: 1.00  Bytes: 0
***************************************
OPTIMIZER STATISTICS AND COMPUTATIONS
***************************************
GENERAL PLANS
***************************************
Considering cardinality-based initial join order.
Permutations for Starting Table :0
***********************
Join order[1]:  TEST[TEST]#0
***********************
Best so far: Table#: 0  cost: 3098.0217  card: 1.0000  bytes: 10
(newjo-stop-1) k:0, spcnt:0, perm:1, maxperm:80000
*********************************
Number of join permutations tried: 1
*********************************
Final - All Rows Plan:  Best join order: 1
  Cost: 3098.0217  Degree: 1  Card: 1.0000  Bytes: 10
  Resc: 3098.0217  Resc_io: 2360.0000  Resc_cpu: 2153524223
  Resp: 3098.0217  Resp_io: 2360.0000  Resc_cpu: 2153524223
kkoipt: Query block SEL$1 (#0)
******* UNPARSED QUERY IS *******
SELECT "TEST"."ID" "ID","TEST"."TEXT" "TEXT" FROM "SYS"."TEST" "TEST" WHERE "TEST"."TEXT"<>'star'
kkoqbc-subheap (delete addr=ffffffff7b11c008, in-use=11712, alloc=26392)
kkoqbc-end
          : call(in-use=15256, alloc=49184), compile(in-use=37792, alloc=40520)
apadrv-end: call(in-use=15256, alloc=49184), compile(in-use=38608, alloc=40520)

sql_id=192f8vs3fqvpc.
Current SQL statement for this session:
Select * from test where text<>'star'

============
Plan Table
============
-------------------------------------+-----------------------------------+
| Id  | Operation          | Name    | Rows  | Bytes | Cost  | Time      |
-------------------------------------+-----------------------------------+
| 0   | SELECT STATEMENT   |         |       |       |  3098 |           |
| 1   |  TABLE ACCESS FULL | TEST    |     1 |    10 |  3098 |  00:00:38 |
-------------------------------------+-----------------------------------+
Predicate Information:
----------------------
1 - filter("TEXT"<>'star')

注意access pass 這里

 

  Access Path: TableScan
    Cost:  3098.02  Resp: 3098.02  Degree: 0
      Cost_io: 2360.00  Cost_cpu: 2153524223
      Resp_io: 2360.00  Resp_cpu: 2153524223
  Best:: AccessPath: TableScan
         Cost: 3098.02  Degree: 1  Resp: 3098.02  Card: 1.00  Bytes: 0

根本就沒有去calculate index的開銷。 所以執行計划就是全表掃描。

 

 

 

之前也懷疑過<>這種方式會不走索引,但是不知道為什么沒有當回事,這次一定要記住 非常有用。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM