隐式转换的替代方案不是万能的
Posted bisal(Chen Liu)
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了隐式转换的替代方案不是万能的相关的知识,希望对你有一定的参考价值。
隐式转换相关的历史文章,
《Oracle、SQL Server和MySQL的隐式转换异同》
隐式转换之前谈的比较多了,这个问题如果单从功能测试上,不一定能发现,但是通过执行计划、静态扫描等,还是能找到一些端倪的,归根结底,还是不规范的设计和开发,导致出现的。
有些隐式转换能通过替代方案解决,例如创建函数索引、将左侧的表达式转换到右侧、更改字段类型、更改变量类型等,但是不是说所有的替代方案在所有场景都适用。
创建测试数据,
SQL> create table test_timestmap_date(c1 date, c2 timestamp);
Table Created.
SQL> insert into test_timestmap_date values(sysdate, systimestamp);
1 row created.
SQL> create index idx_test_timestmap_date_01 on test_timestmap_date(c1);
Index Created.
SQL> commit;
Commit Completed.
我们知道,如果是"where date = date",
SQL> select * from test_timestmap_date where c1=sysdate;
...
会使用到索引,
------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 1 (100)| |
| 1 | TABLE ACCESS BY INDEX ROWID BATCHED| TEST_TIMESTMAP_DATE | 1 | 22 | 1 (0)| 00:00:01 |
|* 2 | INDEX RANGE SCAN | IDX_TEST_TIMESTMAP_DATE_01 | 1 | | 1 (0)| 00:00:01 |
------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("C1"=SYSDATE@!)
如果是"where date = timestamp",右值优先级高,可以看到,左值使用了内部函数INTERVAL_FUNCTION,不会用到索引,
SQL> select * from test_timestmap_date where c1=systimestamp;
...
-----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 3 (100)| |
|* 1 | TABLE ACCESS FULL| TEST_TIMESTMAP_DATE | 1 | 22 | 3 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter(SYS_EXTRACT_UTC(INTERNAL_FUNCTION("C1"))=SYS_EXTRACT_UTC(SYSTIMESTAMP(6)))
如果是varchar2、number,通常能通过to_number()函数作为方案让其能用到索引,但是针对date、timestamp类型的,to_timestamp()不会起作用。
如下所示,创建一个to_timestamp()的函数索引,强制将date转成timestamp类型,
SQL> create index idx_test_timestmap_date_02 on test_timestmap_date(to_timestamp(c1, 'yyyy-mm-dd hh24:mi:ss'));
Index Created.
但实际执行,仍采用全表扫描,
SQL> select * from test_timestmap_date where c2 = systimestamp;
...
-----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 3 (100)| |
|* 1 | TABLE ACCESS FULL| TEST_TIMESTMAP_DATE | 1 | 22 | 3 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter(SYS_EXTRACT_UTC(INTERNAL_FUNCTION("C2"))=SYS_EXTRACT_UTC(SYSTIMESTAMP(6)))
因此,针对这种场景,只能通过修改代码,将程序中的变量类型从timestamp改成date,或者将数据库中的字段类型从date改成timestamp,不能通过仅仅创建函数索引解决。
还是最开始说的,大多数隐式转换,是可以通过规范设计和开发,在投产前的环节进行规避,否则就只能等着出现问题,然后尝试各种替代方案了寻求解决了。
近期更新的文章:
文章分类和索引:
以上是关于隐式转换的替代方案不是万能的的主要内容,如果未能解决你的问题,请参考以下文章