or与索引

or与索引


据说or会使索引失效,那么是不是呢?测试一下,每个例子后会跟随相应的解释。


初始:empno列有索引,ename没有
scott@ORA> select empno,ename from emp where  empno=7788 or empno=7369;


     EMPNO ENAME
---------- ----------
      7369 SMITH
      7788 SCOTT




Execution Plan
----------------------------------------------------------
Plan hash value: 2355049923


---------------------------------------------------------------------------------------
| Id  | Operation                    | Name   | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |        |     2 |    40 |     2   (0)| 00:00:01 |
|   1 |  INLIST ITERATOR             |        |       |       |            |          |
|   2 |   TABLE ACCESS BY INDEX ROWID| EMP    |     2 |    40 |     2   (0)| 00:00:01 |
|*  3 |    INDEX UNIQUE SCAN         | PK_EMP |     1 |       |     2   (0)| 00:00:01 |
---------------------------------------------------------------------------------------


Predicate Information (identified by operation id):
---------------------------------------------------


   3 - access("EMPNO"=7369 OR "EMPNO"=7788)


Note
-----
   - dynamic sampling used for this statement (level=2)




Statistics
----------------------------------------------------------
          4  recursive calls
          0  db block gets
         13  consistent gets
          0  physical reads
          0  redo size
        658  bytes sent via SQL*Net to client
        523  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          2  rows processed
啊,走了索引了,不是说会失效么?其实是这样么?不是的。因为这个or连接的两个条件全是一个字段的,or相当于两个条件的合集,两个条件都是可以走索引的,当然可以走了。
scott@ORA> select ename,empno from emp where empno=7788 or ename='SMITH';


ENAME           EMPNO
---------- ----------
SMITH            7369
SCOTT            7788




Execution Plan
----------------------------------------------------------
Plan hash value: 3956160932


--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |     2 |    40 |     3   (0)| 00:00:01 |
|*  1 |  TABLE ACCESS FULL| EMP  |     2 |    40 |     3   (0)| 00:00:01 |
--------------------------------------------------------------------------


Predicate Information (identified by operation id):
---------------------------------------------------


   1 - filter("EMPNO"=7788 OR "ENAME"='SMITH')


Note
-----
   - dynamic sampling used for this statement (level=2)




Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
          8  consistent gets
          0  physical reads
          0  redo size
        658  bytes sent via SQL*Net to client
        523  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          2  rows processed
啊,这个不走索引了,为什么呢?还是那句话,因为or相当于合集,如果第一个条件走索引,那么第二个条件得走全表扫描,还不如直接来一次全表扫描。


既然这样,那我如果给ename列建个索引呢?还会不会使索引失效呢?


scott@ORA> create index idx_emp_ename on emp(ename) online;


Index created.


scott@ORA> select ename,empno from emp where empno=7788 or ename='SMITH';


ENAME           EMPNO
---------- ----------
SMITH            7369
SCOTT            7788




Execution Plan
----------------------------------------------------------
Plan hash value: 1272340143


--------------------------------------------------------------------------------------------------
| Id  | Operation                        | Name          | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                 |               |     2 |    40 |     2   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID     | EMP           |     2 |    40 |     2   (0)| 00:00:01 |
|   2 |   BITMAP CONVERSION TO ROWIDS    |               |       |       |            |          |
|   3 |    BITMAP OR                     |               |       |       |            |          |
|   4 |     BITMAP CONVERSION FROM ROWIDS|               |       |       |            |          |
|*  5 |      INDEX RANGE SCAN            | PK_EMP        |       |       |     1   (0)| 00:00:01 |
|   6 |     BITMAP CONVERSION FROM ROWIDS|               |       |       |            |          |
|*  7 |      INDEX RANGE SCAN            | IDX_EMP_ENAME |       |       |     1   (0)| 00:00:01 |
--------------------------------------------------------------------------------------------------


Predicate Information (identified by operation id):
---------------------------------------------------


   5 - access("EMPNO"=7788)
   7 - access("ENAME"='SMITH')


Note
-----
   - dynamic sampling used for this statement (level=2)




Statistics
----------------------------------------------------------
          5  recursive calls
          0  db block gets
         14  consistent gets
          0  physical reads
          0  redo size
        658  bytes sent via SQL*Net to client
        523  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          2  rows processed
          
看,又走索引了,还是没有使索引失效啊?no,不是这样的,执行计划这次走的是BITMAP CONVERSION TO ROWIDS,位图转换,这个玩意不一定是好东西,有的时候会让执行计划非常烂的。
这个东西具体怎么回事呢?以后再说,现在说简单的,他是把b-tree索引进行了bitmap转换来执行sql的,就这个转换,不言而喻,很多时候问题是很大的。通常都是禁用的。
隐含参数 _b_tree_bitmap_plans可以禁用他。




找到原因就好说了,那就禁用他再看看。
scott@ORA> alter session set "_b_tree_bitmap_plans"=false;


Session altered.


scott@ORA> set autotrace on
scott@ORA> select ename,empno from emp where empno=7788 or ename='SMITH';


ENAME           EMPNO
---------- ----------
SMITH            7369
SCOTT            7788




Execution Plan
----------------------------------------------------------
Plan hash value: 3956160932


--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |     2 |    40 |     3   (0)| 00:00:01 |
|*  1 |  TABLE ACCESS FULL| EMP  |     2 |    40 |     3   (0)| 00:00:01 |
--------------------------------------------------------------------------


Predicate Information (identified by operation id):
---------------------------------------------------


   1 - filter("EMPNO"=7788 OR "ENAME"='SMITH')


Note
-----
   - dynamic sampling used for this statement (level=2)




Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
          8  consistent gets
          0  physical reads
          0  redo size
        658  bytes sent via SQL*Net to client
        523  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          2  rows processed
看,全表扫描了。
那么,正常情况下,这样的我们怎么优化呢?毕竟两个字段都建了索引了啊。  是吧?可以这样
用union去改写。


scott@ORA>  select ename,empno from emp where empno=7788 
  2   union  select ename,empno from emp where ename='SMITH';


ENAME           EMPNO
---------- ----------
SCOTT            7788
SMITH            7369




Execution Plan
----------------------------------------------------------
Plan hash value: 4119156321


-----------------------------------------------------------------------------------------------
| Id  | Operation                     | Name          | Rows  | Bytes | Cost (%CPU)| Time     |
-----------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT              |               |     2 |    40 |     6  (34)| 00:00:01 |
|   1 |  SORT UNIQUE                  |               |     2 |    40 |     6  (34)| 00:00:01 |
|   2 |   UNION-ALL                   |               |       |       |            |          |
|   3 |    TABLE ACCESS BY INDEX ROWID| EMP           |     1 |    20 |     2   (0)| 00:00:01 |
|*  4 |     INDEX UNIQUE SCAN         | PK_EMP        |     1 |       |     1   (0)| 00:00:01 |
|   5 |    TABLE ACCESS BY INDEX ROWID| EMP           |     1 |    20 |     2   (0)| 00:00:01 |
|*  6 |     INDEX RANGE SCAN          | IDX_EMP_ENAME |     1 |       |     1   (0)| 00:00:01 |
-----------------------------------------------------------------------------------------------


Predicate Information (identified by operation id):
---------------------------------------------------


   4 - access("EMPNO"=7788)
   6 - access("ENAME"='SMITH')


Note
-----
   - dynamic sampling used for this statement (level=2)




Statistics
----------------------------------------------------------
          7  recursive calls
          0  db block gets
         15  consistent gets
          2  physical reads
          0  redo size
        658  bytes sent via SQL*Net to client
        523  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          1  sorts (memory)
          0  sorts (disk)
          2  rows processed
这样呢,就可以走索引了,只要你索引选择性可以,肯定是比全表好的多的。另外,为什么用union 而不用union all呢?因为union all是并集就不是合集了,union是去重的。


另外,光用union有的时候也是不行的。比如:我要查工资小于1000或者是10部门的人都有什么工作。
scott@ORA> SELECT JOB FROM EMP WHERE SAL<1000 OR deptno = 10;


JOB
---------
CLERK
MANAGER
PRESIDENT
CLERK
CLERK


scott@ORA> SELECT JOB FROM EMP WHERE SAL<1000 UNION 
  2  SELECT JOB FROM EMP WHERE deptno = 10;


JOB
---------
CLERK
MANAGER
PRESIDENT
看,明显的,不该给我去重的,他也给去了。这样该怎么做的?
其实只需要多处理一下,比如加个唯一标志,然后加一层再把它去掉就行了,不过这个得是唯一的啊,不然又出错了。
rowid,主键,有唯一约束的列,反正只要能保证唯一。 都行


scott@ORA> select job from (
  2  SELECT rowid r, JOB FROM EMP WHERE SAL<1000 UNION 
  3  SELECT rowid r, JOB FROM EMP WHERE deptno = 10 );


JOB
---------
CLERK
MANAGER
CLERK
PRESIDENT
CLERK


收工


 

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/30123160/viewspace-2121370/,如需转载,请注明出处,否则将追究法律责任。

转载于:http://blog.itpub.net/30123160/viewspace-2121370/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值