[20140111]trunc与函数索引.txt

简介: 经常看sql语句,经常会遇到使用函数trunc(create_date)的情况,这种情况经常出现。 很多情况下,是开发人员的无知,实际上修改为create_date between trunc(sysdate) and trunc(sysdate+1)-1/86400就ok了。

经常看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));

SCOTT@test01p> select * from emp where hiredate=to_date('1980/12/17','yyyy-mm-dd');

     EMPNO ENAME      JOB              MGR HIREDATE                   SAL       COMM     DEPTNO
---------- ---------- --------- ---------- ------------------- ---------- ---------- ----------
      7369 SMITH      CLERK           7902 1980-12-17 00:00:00        800                    20

SCOTT@test01p> @dpc '' ''
PLAN_TABLE_OUTPUT
-------------------------------------
SQL_ID  1ct8dum3uyy7u, child number 0
-------------------------------------
select * from emp where hiredate=to_date('1980/12/17','yyyy-mm-dd')

Plan hash value: 4059437819

-------------------------------------------------------------------------------------
| Id  | Operation                           | Name            | E-Rows | Cost (%CPU)|
-------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                    |                 |        |     2 (100)|
|*  1 |  TABLE ACCESS BY INDEX ROWID BATCHED| EMP             |      1 |     2   (0)|
|*  2 |   INDEX RANGE SCAN                  | IF_EMP_HIREDATE |      1 |     1   (0)|
-------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - filter("HIREDATE"=TO_DATE(' 1980-12-17 00:00:00', 'syyyy-mm-dd
              hh24:mi:ss'))
   2 - access("EMP"."SYS_NC00009$"=TRUNC(TO_DATE(' 1980-12-17 00:00:00',
              'syyyy-mm-dd hh24:mi:ss')))

--可以发现查询使用函数索引,即使是查询条件hiredate=to_date('1980/12/17','yyyy-mm-dd'),这种情况与使用substr(a,1,N)一样。
--即使使用hiredate between to_date('1980/12/17','yyyy-mm-dd')-1 and to_date('1980/12/17','yyyy-mm-dd')+1;条件也一样。

PLAN_TABLE_OUTPUT
-------------------------------------
SQL_ID  0phtjuq0q7u8u, child number 0
-------------------------------------
select * from emp where hiredate between
to_date('1980/12/17','yyyy-mm-dd')-1 and
to_date('1980/12/17','yyyy-mm-dd')+1

Plan hash value: 4059437819

-------------------------------------------------------------------------------------
| Id  | Operation                           | Name            | E-Rows | Cost (%CPU)|
-------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                    |                 |        |     2 (100)|
|*  1 |  TABLE ACCESS BY INDEX ROWID BATCHED| EMP             |      1 |     2   (0)|
|*  2 |   INDEX RANGE SCAN                  | IF_EMP_HIREDATE |      1 |     1   (0)|
-------------------------------------------------------------------------------------

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

   1 - filter(("HIREDATE"              hh24:mi:ss') AND "HIREDATE">=TO_DATE(' 1980-12-16 00:00:00', 'syyyy-mm-dd
              hh24:mi:ss')))
   2 - access("EMP"."SYS_NC00009$">=TRUNC(TO_DATE(' 1980-12-16 00:00:00',
              'syyyy-mm-dd hh24:mi:ss')) AND "EMP"."SYS_NC00009$"              1980-12-18 00:00:00', 'syyyy-mm-dd hh24:mi:ss')))


--反过来讲并不是讲鼓励大家建立这样的函数索引,而是如果应用中已经存在trunc(hiredate)这样的条件,并且hiredate = :1或者
--hiredate between :b1 and :b2这样的查询条件存在,要考虑是否可以减少1个索引的建立,比如如果trunc(create_date)条件返回在
--100条以内,也许create_date这样的索引可以不建立,同样可以获得很好的效果,当然最好测试评估。另外这样的函数索引重复值很
--高,可以选择索引前缀压缩。

SCOTT@test01p> @ver
BANNER                                                                               CON_ID
-------------------------------------------------------------------------------- ----------
Oracle Database 12c Enterprise Edition Release 12.1.0.1.0 - 64bit Production              0

SCOTT@test> @ver
BANNER
--------------------------------------------------------------------------------
Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production

create table t as select rownum id ,sysdate-100+rownum/100 create_date,lpad('X',80,'X') vc from dual connect by levelselect trunc(create_date),count(*) from t group by trunc(create_date) order by 1;
create index if_t_create_date on t (trunc(create_date)) compress 1;
create index i_t_create_date on t (create_date) ;
exec dbms_stats.gather_table_stats(user,'T');

select /*+ gather_plan_statistics */ id,create_date,substr(vc,1,20) c20 from t where  create_date between trunc(sysdate) and trunc(sysdate+1)-1/86400;

SCOTT@test> @dpc '' ''
PLAN_TABLE_OUTPUT
-------------------------------------
SQL_ID  actssvadvtbsd, child number 0
-------------------------------------
select /*+ gather_plan_statistics */ id,create_date,substr(vc,1,20) c20
from t where  create_date between trunc(sysdate) and
trunc(sysdate+1)-1/86400

Plan hash value: 3730485863

-----------------------------------------------------------------------------------------------------------------------
| Id  | Operation                    | Name            | Starts | E-Rows | Cost (%CPU)| A-Rows |   A-Time   | Buffers |
-----------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |                 |      1 |        |     3 (100)|     38 |00:00:00.01 |       5 |
|*  1 |  FILTER                      |                 |      1 |        |            |     38 |00:00:00.01 |       5 |
|   2 |   TABLE ACCESS BY INDEX ROWID| T               |      1 |     39 |     3   (0)|     38 |00:00:00.01 |       5 |
|*  3 |    INDEX RANGE SCAN          | I_T_CREATE_DATE |      1 |     39 |     2   (0)|     38 |00:00:00.01 |       3 |
-----------------------------------------------------------------------------------------------------------------------

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

   1 - filter(TRUNC(SYSDATE@!)   3 - access("CREATE_DATE">=TRUNC(SYSDATE@!) AND "CREATE_DATE"              407407407407407407)

--可以发现这样逻辑读仅仅5个。使用普通索引.

select /*+ gather_plan_statistics index(t if_t_create_date) */ id,create_date,substr(vc,1,20) c20 from t
where  create_date between trunc(sysdate) and trunc(sysdate+1)-1/86400;

SCOTT@test> @dpc '' ''
PLAN_TABLE_OUTPUT
-------------------------------------
SQL_ID  anu23m7rj13cx, child number 0
-------------------------------------
select /*+ gather_plan_statistics index(t if_t_create_date) */
id,create_date,substr(vc,1,20) c20 from t where  create_date between
trunc(sysdate) and trunc(sysdate+1)-1/86400

Plan hash value: 231836870

------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                    | Name             | Starts | E-Rows | Cost (%CPU)| A-Rows |   A-Time   | Buffers |
------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |                  |      1 |        |     4 (100)|     38 |00:00:00.01 |       5 |
|*  1 |  FILTER                      |                  |      1 |        |            |     38 |00:00:00.01 |       5 |
|*  2 |   TABLE ACCESS BY INDEX ROWID| T                |      1 |     39 |     4   (0)|     38 |00:00:00.01 |       5 |
|*  3 |    INDEX RANGE SCAN          | IF_T_CREATE_DATE |      1 |     99 |     2   (0)|     38 |00:00:00.01 |       3 |
------------------------------------------------------------------------------------------------------------------------

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

   1 - filter(TRUNC(SYSDATE@!)   2 - filter(("CREATE_DATE">=TRUNC(SYSDATE@!) AND "CREATE_DATE"              407407407407407407))
   3 - access("T"."SYS_NC00004$">=TRUNC(TRUNC(SYSDATE@!)) AND
              "T"."SYS_NC00004$"

--上下执行对比,cost一样。
--改名
alter index  i_t_create_date rename to iz_t_create_date;

select /*+ gather_plan_statistics */ id,create_date,substr(vc,1,20) c20 from t where  create_date between trunc(sysdate) and trunc(sysdate+1)-1/86400;

SCOTT@test> @dpc '' ''
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------------------
SQL_ID  actssvadvtbsd, child number 0
-------------------------------------
select /*+ gather_plan_statistics */ id,create_date,substr(vc,1,20) c20
from t where  create_date between trunc(sysdate) and
trunc(sysdate+1)-1/86400

Plan hash value: 1209507809

------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                    | Name             | Starts | E-Rows | Cost (%CPU)| A-Rows |   A-Time   | Buffers |
------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |                  |      1 |        |     3 (100)|     38 |00:00:00.01 |       5 |
|*  1 |  FILTER                      |                  |      1 |        |            |     38 |00:00:00.01 |       5 |
|   2 |   TABLE ACCESS BY INDEX ROWID| T                |      1 |     39 |     3   (0)|     38 |00:00:00.01 |       5 |
|*  3 |    INDEX RANGE SCAN          | IZ_T_CREATE_DATE |      1 |     39 |     2   (0)|     38 |00:00:00.01 |       3 |
------------------------------------------------------------------------------------------------------------------------

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

   1 - filter(TRUNC(SYSDATE@!)   3 - access("CREATE_DATE">=TRUNC(SYSDATE@!) AND "CREATE_DATE"              07407407407407407)

-- 可以发现如果索引名字重新排序,优先选择依旧是普通索引。
-- 如果数据聚集性很好,逻辑读会很小。
-- 如果数据分布不聚集,大家有兴趣测试一下,不做了。

--换一个思路,大家认为还有什么函数索引也能使用在一般常规的查询呢?我仅仅知道substr(name,1,N) 以及trunc。
--其他的呢?

目录
相关文章
|
SQL 测试技术 索引
[20180509]函数索引问题.txt
[20180509]函数索引问题.txt https://jonathanlewis.wordpress.com/2018/05/07/fbis-dont-exist/ --//重复测试: 1.
1095 0
|
Oracle 关系型数据库 测试技术
[20180416]clob的插入.txt
[20180416]clob的插入.txt --//论坛上有人问的问题,如何限制BLOB字段的大小,用户只能上传15K至30K的图片,超过30K就不给上传. --//链接:http://www.
1044 0
|
SQL Oracle 关系型数据库
[20180415]blob的插入.txt
[20180415]blob的插入.txt --//论坛上有人问的问题,如何限制BLOB字段的大小,用户只能上传15K至30K的图片,超过30K就不给上传. --//链接:http://www.
1079 0
|
关系型数据库 Linux 索引
[20180212]函数索引问题.txt
[20180212]函数索引问题.txt --//11g下,如果函数索引,字段出现重复,出现ORA-54015: Duplicate column expression was specified.
981 0
|
存储 Oracle 关系型数据库
[20171218]varchar2(4000)如何保存.txt
varchar2(4000)如何保存
1256 0
|
索引 关系型数据库 Oracle
[20171202]关于函数索引的状态.txt
[20171202]关于函数索引的状态.txt --//我曾经在一篇贴子提到索引可以disable吗?链接: --//http://blog.itpub.net/267265/viewspace-2123537/ --//实际上仅仅函数索引能disable,为什么呢?实际上自己以前并不搞清楚实际上这个跟oracle使用函数的特殊性有关.
1212 0
[20170525]分析函数first_value.txt
[20170525]分析函数first_value.txt --//昨天看sql语句,发现居然分析函数first_value.我一直认为开发如果要做一些报表需要了解学习一些oracle分析函数的知识,我发现许 --//多开发这方面一篇空白.
804 0
|
关系型数据库 Oracle 开发工具
[20170508]listagg拼接显示字段.txt
[20170508]listagg拼接显示字段.txt --//记得前一阵子,要给表增加一个字段,并赋值.采用表在线重定义.要使用函数dbms_redefinition.
865 0
|
Oracle 关系型数据库 OLAP
[20170315]11.2.0.4 exp可以导出空表.txt
[20170315]11.2.0.4 exp可以导出空表.txt --链接http://www.itpub.net/thread-2084282-1-1.html,11.
1165 0
|
SQL Oracle 关系型数据库
[20150803]使用函数索引注意的问题.txt
[20150803]使用函数索引注意的问题.txt --昨天在10g下优化时遇到一个奇怪的问题,做一个记录: --首先说明一下,最近一段时间在做一个项目的优化,这个项目实际上ETL项目: http://baike.
842 0