Luk*_*der 12 oracle timestamp jdbc sql-execution-plan
我正在分析Oracle执行计划,并发现了一个令人惊讶的事实.看看这个查询.提示只是为了显示我有一个索引,我希望Oracle将它用于范围扫描:
// execute_at is of type DATE.
PreparedStatement stmt = connection.prepareStatement(
"SELECT /*+ index(my_table my_index) */ * " +
"FROM my_table " +
"WHERE execute_at > ? AND execute_at < ?");
Run Code Online (Sandbox Code Playgroud)
这两个绑定导致完全不同的行为(排除绑定变量偷看问题,我实际上强制执行两个硬解析):
// 1. with timestamps
stmt.setTimestamp(1, start);
stmt.setTimestamp(2, end);
// 2. with dates
stmt.setDate(1, start);
stmt.setDate(2, end);
Run Code Online (Sandbox Code Playgroud)
1)有了时间戳,我得到INDEX FULL SCAN一个过滤谓词
--------------------------------------------------------------
| Id | Operation | Name |
--------------------------------------------------------------
| 0 | SELECT STATEMENT | |
|* 1 | FILTER | |
| 2 | TABLE ACCESS BY INDEX ROWID| my_table |
|* 3 | INDEX FULL SCAN | my_index |
--------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter(:1<:2)"
3 - filter((INTERNAL_FUNCTION(""EXECUTE_AT"")>:1 AND
INTERNAL_FUNCTION(""EXECUTE_AT"")<:2))
Run Code Online (Sandbox Code Playgroud)
2)对于日期,我得到了更好的INDEX RANGE SCAN和访问谓词
--------------------------------------------------------------
| Id | Operation | Name |
--------------------------------------------------------------
| 0 | SELECT STATEMENT | |
|* 1 | FILTER | |
| 2 | TABLE ACCESS BY INDEX ROWID| my_table |
|* 3 | INDEX RANGE SCAN | my_index |
--------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter(:1<:2)"
3 - access(""EXECUTE_AT"">:1 AND ""EXECUTE_AT""<:2)
Run Code Online (Sandbox Code Playgroud)
现在我的例子就是一个例子.真正的查询要复杂得多,必须有RANGE SCANS或UNIQUE SCANS(取决于谓词)而不是FULL SCANS.
我有什么误解吗?有人能指出我最好的解决方案/做法吗?因为在Java世界中,我认为这java.sql.Timestamp更合适,但我们的大多数列都是Oracle的DATE类型.我们使用的是Java 6和Oracle 11g
APC*_*APC 15
所以问题是,Oracle时间戳和Oracle日期是两种不同的数据类型.为了时间戳比较日期Oracle必须运行转换 - 即INTERNAL_FUNCTION().有趣的设计决策是Oracle转换表列而不是传递的值,这意味着查询不再使用索引.
我已经能够在SQL*Plus中重现您的场景,因此使用它不是问题java.sql.Timestamp.将传递的时间戳转换为日期确实解决了问题...
SQL> explain plan for
2 select * from test1
3 where d1 > cast(to_timestamp('01-MAY-2011 00:00:00.000', 'DD-MON-YYYY Hh24:MI:SS.FF') as date)
4 and d2 > cast(to_timestamp('01-JUN-2011 23:59:59.999', 'DD-MON-YYYY Hh24:MI:SS.FF') as date)
5 /
Explained.
SQL> select * from table(dbms_xplan.display)
2 /
PLAN_TABLE_OUTPUT
-----------------------------------------------------------
Plan hash value: 1531258174
-------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 25 | 500 | 3 (0)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID| TEST1 | 25 | 500 | 3 (0)| 00:00:01 |
|* 2 | INDEX RANGE SCAN | T1_I | 1 | | 2 (0)| 00:00:01 |
-------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
PLAN_TABLE_OUTPUT
-----------------------------------------------------------------------------------
2 - access("D1">CAST(TO_TIMESTAMP('01-MAY-2011 00:00:00.000','DD-MON-YYYY
Hh24:MI:SS.FF') AS date) AND "D2">CAST(TO_TIMESTAMP('01-JUN-2011
23:59:59.999','DD-MON-YYYY Hh24:MI:SS.FF') AS date) AND "D1" IS NOT NULL)
filter("D2">CAST(TO_TIMESTAMP('01-JUN-2011 23:59:59.999','DD-MON-YYYY
Hh24:MI:SS.FF') AS date))
18 rows selected.
SQL>
Run Code Online (Sandbox Code Playgroud)
但我认为这对你没有任何帮助:只是简单地传递日期会更容易.
有趣的是,构建一个基于函数的索引将日期列转换为时间戳并没有帮助.该INTERNAL_FUNCTION()调用未被识别为a CAST()并且忽略该索引.尝试使用INTERNAL_FUNCTION()ORA-00904投掷建立索引.
| 归档时间: |
|
| 查看次数: |
4235 次 |
| 最近记录: |