[20150803]使用函数索引注意的问题.txt

[20150803]使用函数索引注意的问题.txt

--昨天在10g下优化时遇到一个奇怪的问题,做一个记录:
--首先说明一下,最近一段时间在做一个项目的优化,这个项目实际上ETL项目:

http://baike.baidu.com/link?url=OlbL-2LIVu06toxpf5-PxgekWlOtRgrdwPhGYNx9TgCnCC5WdAGiwOWQXcfUbujcUNwUU6ojdanwP1wSbC_Vf95sgbq7PonHaEZWBVrqkQm

ETL,是英文 Extract-Transform-Load 的缩写,用来描述将数据从来源端经过抽取(extract)、转换(transform)、加载(load)至
目的端的过程。ETL一词较常用在数据仓库,但其对象并不限于数据仓库。

ETL是构建数据仓库的重要一环,用户从数据源抽取出所需的数据,经过数据清洗,最终按照预先定义好的数据仓库模型,将数据加载到数
据仓库中去。

信息是现代企业的重要资源,是企业运用科学管理、决策分析的基础。目前,大多数企业花费大量的资金和时间来构建联机事务处理OLTP
的业务系统和办公自动化系统,用来记录事务处理的各种相关数据。据统计,数据量每2~3年时间就会成倍增长,这些数据蕴含着巨大的
商业价值,而企业所关注的通常只占在总数据量的2%~4%左右。因此,企业仍然没有最大化地利用已存在的数据资源,以致于浪费了更多
的时间和资金,也失去制定关键商业决策的最佳契机。于是,企业如何通过各种技术手段,并把数据转换为信息、知识,已经成了提高其
核心竞争力的主要瓶颈。而ETL则是主要的一个技术手段。

--我个人对应这些不是非常熟悉,里面存在大量视图,标量子查询.我现在主要是优化抽取这部分的sql语句,但是我觉得开发在处理存在问
--题,应该在前端处理的信息,而不应该放在抽取以后再处理.好了,先不说这里,看看例子来说明问题:

1.建立测试环境:
SCOTT@test01p> @ ver1

PORT_STRING           VERSION        BANNER                                                                        CON_ID
--------------------- -------------- ----------------------------------------------------------------------------- ------
IBMPC/WIN_NT64-9.1.0  12.1.0.1.0     Oracle Database 12c Enterprise Edition Release 12.1.0.1.0 - 64bit Production       0

--家里只有12c for windows 版本,问题应该能重复再现的.

create table t1 (id number ,idx number, pad varchar2(20));
insert into t1 select rownum,trunc(rownum/10),lpad('a',20,'a') from xmltable('1 to 100000');
commit ;

create table t2 (idx number, padx varchar2(20));
insert into t2 select rownum,lpad('b',10,'b') from xmltable('1 to 10000');
commit ;

update t2 set padx=null where idx=42;
commit ;

create unique index pk_t1 on t1 (id);
alter table t1 add constraint pk_t1 primary key (id);

create unique index pk_t2 on t2 (idx);
alter table t2 add constraint pk_t2 primary key (idx);

create index i_t1_idx on t1(idx);

--IDX字段在表T2:T1的数量比例是1:10.
--分析忽略.实际上的表比以上要大好几倍.

2.开始执行;
SCOTT@test01p> alter session set statistics_level=all;
Session altered.

SCOTT@test01p> alter session set optimizer_adaptive_features=false;
Session altered.
--注意关闭主要是避免出现adaptive  plan计划.

select * from t1 where t1.idx in ( select idx from t2 where padx is null);
--说明一下,实际上在生产系统执行的是delete语句,我这里换成了select.
--这也是我对开发处理信息非常不理解的原因,明明这些应该过滤掉的信息,为什么在插入后来处理.这明显的不好.

Plan hash value: 3237420647
-----------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                    | Name     | Starts | E-Rows |E-Bytes| Cost (%CPU)| E-Time   | A-Rows |   A-Time   | Buffers |
-----------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |          |      1 |        |       |    11 (100)|          |     10 |00:00:00.01 |      36 |
|   1 |  NESTED LOOPS                |          |      1 |        |       |            |          |     10 |00:00:00.01 |      36 |
|   2 |   NESTED LOOPS               |          |      1 |     10 |   450 |    11   (0)| 00:00:01 |     10 |00:00:00.01 |      34 |
|*  3 |    TABLE ACCESS FULL         | T2       |      1 |      1 |    15 |     9   (0)| 00:00:01 |      1 |00:00:00.01 |      31 |
|*  4 |    INDEX RANGE SCAN          | I_T1_IDX |      1 |     10 |       |     1   (0)| 00:00:01 |     10 |00:00:00.01 |       3 |
|   5 |   TABLE ACCESS BY INDEX ROWID| T1       |     10 |     10 |   300 |     2   (0)| 00:00:01 |     10 |00:00:00.01 |       2 |
-----------------------------------------------------------------------------------------------------------------------------------
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
   3 - SEL$5DA710D3 / T2@SEL$2
   4 - SEL$5DA710D3 / T1@SEL$1
   5 - SEL$5DA710D3 / T1@SEL$1
Predicate Information (identified by operation id):
---------------------------------------------------
   3 - filter("PADX" IS NULL)
   4 - access("T1"."IDX"="IDX")

-- T2 全表扫描,实际上padx是null很少,可以通过改写仅仅包含null的索引来避开t2的全表扫描.(我没有办法先暂时优化这个语句)
-- 注意: T1表是可以使用索引I_T1_IDX的.

3.建立索引看看.

create index if_t2_padx on t2(decode(padx,null,'0'));

--sql改写如下:

select * from t1 where t1.idx in ( select idx from t2 where decode(padx,null,'0') ='0');

SCOTT@test01p> @dpc '' ''
PLAN_TABLE_OUTPUT
-------------------------------------
SQL_ID  dz5cfzwv95hrk, child number 0
-------------------------------------
select * from t1 where t1.idx in ( select idx from t2 where
decode(padx,null,'0') ='0')
Plan hash value: 3096347206
------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                            | Name       | Starts | E-Rows |E-Bytes| Cost (%CPU)| E-Time   | A-Rows |   A-Time   | Buffers |  OMem |  1Mem | Used-Mem |
------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                     |            |      1 |        |       |   139 (100)|          |     10 |00:00:00.02 |     503 |       |       |          |
|*  1 |  HASH JOIN                           |            |      1 |   1000 | 36000 |   139   (1)| 00:00:01 |     10 |00:00:00.02 |     503 |  2440K|  2440K|  693K (0)|
|   2 |   TABLE ACCESS BY INDEX ROWID BATCHED| T2         |      1 |    100 |   600 |     2   (0)| 00:00:01 |      1 |00:00:00.01 |       2 |       |       |          |
|*  3 |    INDEX RANGE SCAN                  | IF_T2_PADX |      1 |      1 |       |     1   (0)| 00:00:01 |      1 |00:00:00.01 |       1 |       |       |          |
|   4 |   TABLE ACCESS FULL                  | T1         |      1 |    100K|  2929K|   137   (1)| 00:00:01 |    100K|00:00:00.01 |     501 |       |       |          |
------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
   1 - SEL$5DA710D3
   2 - SEL$5DA710D3 / T2@SEL$2
   3 - SEL$5DA710D3 / T2@SEL$2
   4 - SEL$5DA710D3 / T1@SEL$1
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - access("T1"."IDX"="IDX")
   3 - access("T2"."SYS_NC00003$"='0')

--注意看连接选择了hash join,T1不再使用索引,而是选择了全表扫描.实际上在我们的生产环境更加糟糕.
--走的是nested loop,而且是先全表扫描T1,再探查T2(通过索引IF_T2_PADX).

--生产系统看到如下:
SYSTEM@192.168.100.88:1521/tyt> @dpc '' ''
PLAN_TABLE_OUTPUT
-------------------------------------
SQL_ID  axxunnb37urzm, child number 1
-------------------------------------
select * from presc_check_detail where PRESC_CHECK_ID in (select id from presc_check where decode(diagnosis,NULL,'0')='0' )
Plan hash value: 2672249901
------------------------------------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                    | Name                     | Starts | E-Rows |E-Bytes| Cost (%CPU)| E-Time   | A-Rows |   A-Time   | Buffers | Reads  |
------------------------------------------------------------------------------------------------------------------------------------------------------------
|   1 |  NESTED LOOPS                |                          |      1 |    263K|    35M| 37842   (1)| 00:07:35 |      0 |00:03:42.59 |     169K|    169K|
|   2 |   TABLE ACCESS FULL          | PRESC_CHECK_DETAIL       |      1 |     12M|  1042M| 37738   (1)| 00:07:33 |     12M|00:00:25.63 |     169K|    169K|
|*  3 |   TABLE ACCESS BY INDEX ROWID| PRESC_CHECK              |     12M|      1 |    55 |     0   (0)|          |      0 |00:02:27.75 |       2 |      0 |
|*  4 |    INDEX RANGE SCAN          | IF_PRESC_CHECK_DIAG_NULL |     12M|      1 |       |     0   (0)|          |      0 |00:01:03.21 |       2 |      0 |
------------------------------------------------------------------------------------------------------------------------------------------------------------
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
   1 - SEL$5DA710D3
   2 - SEL$5DA710D3 / PRESC_CHECK_DETAIL@SEL$1
   3 - SEL$5DA710D3 / PRESC_CHECK@SEL$2
   4 - SEL$5DA710D3 / PRESC_CHECK@SEL$2
Predicate Information (identified by operation id):
---------------------------------------------------
   3 - filter("PRESC_CHECK_ID"="ID")
   4 - access("PRESC_CHECK"."SYS_NC00071$"='0')

--要3:42分钟才出结果.注:不知道为什么没有SELECT STATEMENT 部分.而让我吃惊的是id=3,4 A-rows的估计都是正确的,为什么不先探
--查PRESC_CHECK,然后是PRESC_CHECK_DETAIL.注意看A-Rows=0(ID=4).

3.为了接近真实环境.我建立另外的表T3.

create table t3 (idx number, padx varchar2(20));
insert into t3 select rownum,lpad('b',10,'b') from xmltable('1 to 10000');
commit ;

create unique index pk_t3 on t3 (idx);
alter table t3 add constraint pk_t3 primary key (idx);
--分析表T3.
create index if_t3_padx on t3(decode(padx,null,'0'));

select * from t1 where t1.idx in ( select idx from t3 where decode(padx,null,'0') ='0');

Plan hash value: 2725908636
---------------------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                            | Name       | Starts | E-Rows |E-Bytes| Cost (%CPU)| E-Time   | A-Rows |   A-Time   | Buffers |
---------------------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                     |            |      1 |        |       |   137 (100)|          |      0 |00:00:00.16 |     501 |
|   1 |  NESTED LOOPS                        |            |      1 |   1000 | 36000 |   137   (1)| 00:00:01 |      0 |00:00:00.16 |     501 |
|   2 |   TABLE ACCESS FULL                  | T1         |      1 |    100K|  2929K|   137   (1)| 00:00:01 |    100K|00:00:00.02 |     500 |
|*  3 |   TABLE ACCESS BY INDEX ROWID BATCHED| T3         |    100K|      1 |     6 |     0   (0)|          |      0 |00:00:00.10 |       1 |
|*  4 |    INDEX RANGE SCAN                  | IF_T3_PADX |    100K|      1 |       |     0   (0)|          |      0 |00:00:00.04 |       1 |
---------------------------------------------------------------------------------------------------------------------------------------------
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
   1 - SEL$5DA710D3
   2 - SEL$5DA710D3 / T1@SEL$1
   3 - SEL$5DA710D3 / T3@SEL$2
   4 - SEL$5DA710D3 / T3@SEL$2
Predicate Information (identified by operation id):
---------------------------------------------------
   3 - filter("T1"."IDX"="IDX")
   4 - access("T3"."SYS_NC00003$"='0')
31 rows selected.

--^_^,现在完美了再现生产系统遇到的问题. 看到这样的执行计划开始实在搞不懂为什么?
--当时赶着回家,修改如下:
select * from t1 where t1.idx in ( select idx from t3 where decode(padx,null,'0') ='0' and padx is null);

Plan hash value: 2730966104
----------------------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                             | Name       | Starts | E-Rows |E-Bytes| Cost (%CPU)| E-Time   | A-Rows |   A-Time   | Buffers |
----------------------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                      |            |      1 |        |       |     3 (100)|          |      0 |00:00:00.01 |       1 |
|   1 |  NESTED LOOPS                         |            |      1 |        |       |            |          |      0 |00:00:00.01 |       1 |
|   2 |   NESTED LOOPS                        |            |      1 |      1 |    45 |     3   (0)| 00:00:01 |      0 |00:00:00.01 |       1 |
|*  3 |    TABLE ACCESS BY INDEX ROWID BATCHED| T3         |      1 |      1 |    15 |     1   (0)| 00:00:01 |      0 |00:00:00.01 |       1 |
|*  4 |     INDEX RANGE SCAN                  | IF_T3_PADX |      1 |      1 |       |     1   (0)| 00:00:01 |      0 |00:00:00.01 |       1 |
|*  5 |    INDEX RANGE SCAN                   | I_T1_IDX   |      0 |     10 |       |     1   (0)| 00:00:01 |      0 |00:00:00.01 |       0 |
|   6 |   TABLE ACCESS BY INDEX ROWID         | T1         |      0 |     10 |   300 |     2   (0)| 00:00:01 |      0 |00:00:00.01 |       0 |
----------------------------------------------------------------------------------------------------------------------------------------------
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
   1 - SEL$5DA710D3
   3 - SEL$5DA710D3 / T3@SEL$2
   4 - SEL$5DA710D3 / T3@SEL$2
   5 - SEL$5DA710D3 / T1@SEL$1
   6 - SEL$5DA710D3 / T1@SEL$1
Predicate Information (identified by operation id):
---------------------------------------------------
   3 - filter("PADX" IS NULL)
   4 - access("T3"."SYS_NC00003$"='0')
   5 - access("T1"."IDX"="IDX")

--为什么加入padx is null条件,oracle走的执行计划正确呢? 这样推测条件 decode(padx,null,'0') ='0' 估计的返回行数不对.

4.看看如下执行计划:

SCOTT@test01p> select idx from t3 where decode(padx,null,'0') ='0';
no rows selected

SCOTT@test01p> @dpc '' ''
PLAN_TABLE_OUTPUT
-------------------------------------
SQL_ID  c7rx9t731yzus, child number 0
-------------------------------------
select idx from t3 where decode(padx,null,'0') ='0'
Plan hash value: 3619030488
--------------------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                           | Name       | Starts | E-Rows |E-Bytes| Cost (%CPU)| E-Time   | A-Rows |   A-Time   | Buffers |
--------------------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                    |            |      1 |        |       |     1 (100)|          |      0 |00:00:00.01 |       1 |
|   1 |  TABLE ACCESS BY INDEX ROWID BATCHED| T3         |      1 |    100 |   600 |     1   (0)| 00:00:01 |      0 |00:00:00.01 |       1 |
|*  2 |   INDEX RANGE SCAN                  | IF_T3_PADX |      1 |      1 |       |     1   (0)| 00:00:01 |      0 |00:00:00.01 |       1 |
--------------------------------------------------------------------------------------------------------------------------------------------
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
   1 - SEL$1 / T3@SEL$1
   2 - SEL$1 / T3@SEL$1
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - access("T3"."SYS_NC00003$"='0')

-- 可以看出通过查询到表的行数是E-Rows=100.我T2 总记录10000行,按1%来估算.可以修改统计来验证这个问题:
SCOTT@test01p> EXECUTE SYS.DBMS_STATS.SET_TABLE_STATS (OWNNAME=>user, TABNAME=>'T3', NUMROWS=> 1e6);
PL/SQL procedure successfully completed.

SCOTT@test01p> @dpc '' ''
PLAN_TABLE_OUTPUT
-------------------------------------
SQL_ID  c7rx9t731yzus, child number 0
-------------------------------------
select idx from t3 where decode(padx,null,'0') ='0'
Plan hash value: 3619030488
--------------------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                           | Name       | Starts | E-Rows |E-Bytes| Cost (%CPU)| E-Time   | A-Rows |   A-Time   | Buffers |
--------------------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                    |            |      1 |        |       |     1 (100)|          |      0 |00:00:00.01 |       1 |
|   1 |  TABLE ACCESS BY INDEX ROWID BATCHED| T3         |      1 |  10000 | 60000 |     1   (0)| 00:00:01 |      0 |00:00:00.01 |       1 |
|*  2 |   INDEX RANGE SCAN                  | IF_T3_PADX |      1 |      1 |       |     1   (0)| 00:00:01 |      0 |00:00:00.01 |       1 |
--------------------------------------------------------------------------------------------------------------------------------------------
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
   1 - SEL$1 / T3@SEL$1
   2 - SEL$1 / T3@SEL$1
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - access("T3"."SYS_NC00003$"='0')
--修改行数量1e6,1% 计算就是1e4.

5.查看字段统计信息:

SCOTT@test01p> set NULL NULL
SCOTT@test01p> select column_name,num_distinct,low_value,high_value,density,num_nulls,num_buckets from DBA_TAB_COLS where owner=user and table_name='T3';
COLUMN_NAME          NUM_DISTINCT LOW_VALUE  HIGH_VALUE    DENSITY  NUM_NULLS NUM_BUCKETS
-------------------- ------------ ---------- ---------- ---------- ---------- -----------
IDX                         10000 C102       C302            .0001          0           1
PADX                            1 6262626262 6262626262          1          0           1
                                  6262626262 6262626262

SYS_NC00003$         NULL         NULL       NULL       NULL       NULL       NULL

--才发现在建立函数索引时实际上是建立了一个隐含列SYS_NC00003$.而建立索引并不会自动建立该列的相关统计信息.
--如果仔细看可以发现我分析T3表后在建立函数索引的.

exec sys.dbms_stats.gather_table_stats ( OwnName => user,TabName => 't3',Estimate_Percent => NULL,Method_Opt => 'FOR ALL COLUMNS SIZE 1 ',Cascade => True ,No_Invalidate => false)
exec sys.dbms_stats.gather_table_stats ( OwnName => user,TabName => 't2',Estimate_Percent => NULL,Method_Opt => 'FOR ALL COLUMNS SIZE 1 ',Cascade => True ,No_Invalidate => false)

SCOTT@test01p> select table_name,column_name,num_distinct,low_value,high_value,density,num_nulls,num_buckets from DBA_TAB_COLS where owner=user and table_name in ('T3','T2');
TABLE_NAME COLUMN_NAME          NUM_DISTINCT LOW_VALUE  HIGH_VALUE    DENSITY  NUM_NULLS NUM_BUCKETS
---------- -------------------- ------------ ---------- ---------- ---------- ---------- -----------
T2         IDX                         10000 C102       C302            .0001          0           1
T2         PADX                            1 6262626262 6262626262          1          1           1
                                             6262626262 6262626262

T2         SYS_NC00003$                    1 30         30                  1       9999           1
T3         IDX                         10000 C102       C302            .0001          0           1
T3         PADX                            1 6262626262 6262626262          1          0           1
                                             6262626262 6262626262

T3         SYS_NC00003$                    0 NULL       NULL                0      10000           0

6 rows selected.

--这样再执行以上语句:

SCOTT@test01p> select * from t1 where t1.idx in ( select idx from t3 where decode(padx,null,'0') ='0');
no rows selected

SCOTT@test01p> @dpc '' ''
PLAN_TABLE_OUTPUT
-------------------------------------
SQL_ID  fzvjhhkhjjukd, child number 0
-------------------------------------
select * from t1 where t1.idx in ( select idx from t3 where
decode(padx,null,'0') ='0')
Plan hash value: 2730966104
----------------------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                             | Name       | Starts | E-Rows |E-Bytes| Cost (%CPU)| E-Time   | A-Rows |   A-Time   | Buffers |
----------------------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                      |            |      1 |        |       |     3 (100)|          |      0 |00:00:00.01 |       1 |
|   1 |  NESTED LOOPS                         |            |      1 |        |       |            |          |      0 |00:00:00.01 |       1 |
|   2 |   NESTED LOOPS                        |            |      1 |      1 |    36 |     3   (0)| 00:00:01 |      0 |00:00:00.01 |       1 |
|   3 |    TABLE ACCESS BY INDEX ROWID BATCHED| T3         |      1 |      1 |     6 |     1   (0)| 00:00:01 |      0 |00:00:00.01 |       1 |
|*  4 |     INDEX RANGE SCAN                  | IF_T3_PADX |      1 |      1 |       |     1   (0)| 00:00:01 |      0 |00:00:00.01 |       1 |
|*  5 |    INDEX RANGE SCAN                   | I_T1_IDX   |      0 |     10 |       |     1   (0)| 00:00:01 |      0 |00:00:00.01 |       0 |
|   6 |   TABLE ACCESS BY INDEX ROWID         | T1         |      0 |     10 |   300 |     2   (0)| 00:00:01 |      0 |00:00:00.01 |       0 |
----------------------------------------------------------------------------------------------------------------------------------------------

Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------

   1 - SEL$5DA710D3
   3 - SEL$5DA710D3 / T3@SEL$2
   4 - SEL$5DA710D3 / T3@SEL$2
   5 - SEL$5DA710D3 / T1@SEL$1
   6 - SEL$5DA710D3 / T1@SEL$1

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

   4 - access("T3"."SYS_NC00003$"='0')
   5 - access("T1"."IDX"="IDX")

--这次选择了正确的执行计划.

--总结:
--在建立函数索引后,应该马上分析一下表,这样相关隐含列才建立统计信息.不然按照等值查询时按照1%估算,这样对于大表1%也是一个很
  大的数值.从而导致选择错误的执行计划.

--补充一些测试:

SCOTT@test01p> column PARTITION_NAME noprint
SCOTT@test01p> column SUBPARTITION_NAME noprint
SCOTT@test01p> select * from DBA_TAB_STATS_HISTORY where owner=user and table_name='T3';
OWNER  TABLE_NAME STATS_UPDATE_TIME
------ ---------- ---------------------------------------------------------------------------
SCOTT  T3         2015-07-31 22:25:32.202000
SCOTT  T3         2015-07-31 23:15:58.690000

SCOTT@test01p> exec dbms_stats.restore_table_stats(user,'T3','2015-07-31 22:25:33');
PL/SQL procedure successfully completed.

--返回旧统计信息:
SCOTT@test01p> select table_name,column_name,num_distinct,low_value,high_value,density,num_nulls,num_buckets from DBA_TAB_COLS where owner=user and table_name in ('T3');
TABLE_NAME COLUMN_NAME          NUM_DISTINCT LOW_VALUE  HIGH_VALUE    DENSITY  NUM_NULLS NUM_BUCKETS
---------- -------------------- ------------ ---------- ---------- ---------- ---------- -----------
T3         IDX                         10000 C102       C302            .0001          0           1
T3         PADX                            1 6262626262 6262626262          1          0           1
                                             6262626262 6262626262

T3         SYS_NC00003$         NULL         NULL       NULL       NULL       NULL       NULL

--加入提示看看:

SCOTT@test01p> select /*+ leading(t3 t1) index(t1 i_t1_idx) */ * from t1 where t1.idx in ( select idx from t3 where decode(padx,null,'0') ='0');

no rows selected

SCOTT@test01p> @dpc '' ''
PLAN_TABLE_OUTPUT
-------------------------------------
SQL_ID  d76wr4rsprr6b, child number 0
-------------------------------------
select /*+ leading(t3 t1) index(t1 i_t1_idx) */ * from t1 where t1.idx
in ( select idx from t3 where decode(padx,null,'0') ='0')
Plan hash value: 2730966104
----------------------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                             | Name       | Starts | E-Rows |E-Bytes| Cost (%CPU)| E-Time   | A-Rows |   A-Time   | Buffers |
----------------------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                      |            |      1 |        |       |   201 (100)|          |      0 |00:00:00.01 |       1 |
|   1 |  NESTED LOOPS                         |            |      1 |        |       |            |          |      0 |00:00:00.01 |       1 |
|   2 |   NESTED LOOPS                        |            |      1 |   1000 | 36000 |   201   (0)| 00:00:01 |      0 |00:00:00.01 |       1 |
|   3 |    TABLE ACCESS BY INDEX ROWID BATCHED| T3         |      1 |    100 |   600 |     1   (0)| 00:00:01 |      0 |00:00:00.01 |       1 |
|*  4 |     INDEX RANGE SCAN                  | IF_T3_PADX |      1 |      1 |       |     1   (0)| 00:00:01 |      0 |00:00:00.01 |       1 |
|*  5 |    INDEX RANGE SCAN                   | I_T1_IDX   |      0 |     10 |       |     1   (0)| 00:00:01 |      0 |00:00:00.01 |       0 |
|   6 |   TABLE ACCESS BY INDEX ROWID         | T1         |      0 |     10 |   300 |     2   (0)| 00:00:01 |      0 |00:00:00.01 |       0 |
----------------------------------------------------------------------------------------------------------------------------------------------
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
   1 - SEL$5DA710D3
   3 - SEL$5DA710D3 / T3@SEL$2
   4 - SEL$5DA710D3 / T3@SEL$2
   5 - SEL$5DA710D3 / T1@SEL$1
   6 - SEL$5DA710D3 / T1@SEL$1
Predicate Information (identified by operation id):
---------------------------------------------------
   4 - access("T3"."SYS_NC00003$"='0')
   5 - access("T1"."IDX"="IDX")

--对比发现id=3,E-rows=100,cost最后=201,比137高不少.在这样的统计信息下,oracle不会选择这样的执行计划.

时间: 2024-10-14 05:16:50

[20150803]使用函数索引注意的问题.txt的相关文章

[20150513]函数索引与CURSOR_SHARING=FORCE

[20150513]函数索引与CURSOR_SHARING=FORCE.txt --经常awr报表,大量听到的建议是你们的应用没有使用绑定变量.国内的许多项目这个问题更加严重,我敢打赌国内80%甚至更高的比例在 --应用中没有绑定变量(OLTP系统). --如果一个新项目我只要看一下程序使用绑定变量的情况,就知道这个项目是垃圾还是豆腐渣工程.到目前为止我接触的项目仅仅有1个做 --的稍微好一点. --如果不修改代码,一个最简单的方式就是修改参数CURSOR_SHARING = FORCE(补充一

[20171202]关于函数索引的状态.txt

[20171202]关于函数索引的状态.txt --//我曾经在一篇贴子提到索引可以disable吗?链接: --//http://blog.itpub.net/267265/viewspace-2123537/ --//实际上仅仅函数索引能disable,为什么呢?实际上自己以前并不搞清楚实际上这个跟oracle使用函数的特殊性有关. --//如果一个表删除某个字段,对应的索引也会删除.如果定义的函数删除了,对应的函数索引呢?通过例子来说明问题: 1.环境: SCOTT@test01p> @

[20170402]函数索引standard_hash.txt

[20170402]函数索引standard_hash.txt --//这几天放假,在家里看书<<Apress.Expert.Oracle.Indexing.and.Access.Paths.Maximum.Performance.for.Your.Database.2nd.Edition.148421983X.pdf>> --//这本书比较合适初学者,我以前看过第一版,所以这个版本看的很快. --//里面提到函数standard_hash,可以用来满足一般的查询,做一个记录. 1

[20120130]函数索引与取max值的问题1.txt

[20120130]函数索引与取max值的问题1.txt 1.建立测试例子:SQL> select * from v$version ;BANNER----------------------------------------------------------------Oracle Database 10g Enterprise Edition Release 10.2.0.3.0 - 64biPL/SQL Release 10.2.0.3.0 - ProductionCORE    10

[20140111]trunc与函数索引.txt

经常看sql语句,经常会遇到使用函数trunc(create_date)的情况,这种情况经常出现. 很多情况下,是开发人员的无知,实际上修改为create_date between trunc(sysdate) and trunc(sysdate+1)-1/86400就ok了. 就可以避免函数索引的建立,但是我这里提到的情况是这种函数索引是否对普通的查询是否有效. 拿scott.emp表来说. create index if_emp_hiredate on emp(trunc(hiredate)

0429建立Extended Statistics函数索引问题

[20160429]建立Extended Statistics 和函数索引问题.txt --11G支持相关数据的统计分析,比如如果两个字段存在相关性通过分析,能够得到更加良好的统计信息,以及生成好的执行计划. --但是如果结合函数索引呢?通过一个简单的例子来说明: --前次做的测试: http://blog.itpub.net/267265/viewspace-2089119/ 1.环境: SCOTT@book> @ &r/ver1 PORT_STRING                  

0427建立Extended Statistics函数索引问题

[20160427]建立Extended Statistics 和函数索引问题.txt --11G支持相关数据的统计分析,比如如果两个字段存在相关性通过分析,能够得到更加良好的统计信息,以及生成好的执行计划. --但是如果结合函数索引呢?通过一个简单的例子来说明: 1.环境: SCOTT@book> @ &r/ver1 PORT_STRING                    VERSION        BANNER ------------------------------ ---

[20121028]IOT的第2索引-NULL的问题.txt

[20121028]IOT的第2索引-NULL的问题.txt IOT表实际上时索引结构,如果第2索引的键值为NULL,会是什么情况呢? 因为第2索引包含主键,而主键是不能为NULL的,这样即使第2索引的键值为NULL,会包括在第2索引中吗? 自己做一些测试验证看看: 1.测试环境: SQL> select * from v$version where rownum BANNER -----------------------------------------------------------

PHP函数索引(3)

函数|索引 mcrypt_ecb: 使用 ECB 将资料加/解密. mcrypt_get_block_size: 取得编码方式的区块大小. mcrypt_get_cipher_name: 取得编码方式的名称. mcrypt_get_key_size: 取得编码钥匙大小. mcrypt_ofb: 使用 OFB 将资料加/解密. md5: 计算字符串的 MD5 哈稀. mhash: 计算哈稀值. mhash_count: 取得哈稀 ID 的最大值. mhash_get_block_size: 取得哈