使用
EXECUTION_ENDED_DATE - EXECUTION_START_DATE > interval '40' minute
另外,重写这个条件
TRUNC(EXECUTION_START_DATE)=TRUNC(SYSDATE)
进入这个
EXECUTION_START_DATE >= TRUNC(SYSDATE) and EXECUTION_START_DATE < TRUNC(SYSDATE)+1
因为前一种情况会阻止 Oracle 在 EXECUTION_START_DATE 列上使用索引并带来全表扫描
----------------
编辑
----------------
INTERVAL 子句仅适用于时间戳算术。
如果查询返回ORA-00932: inconsistent datatypes: expected NUMBER got INTERVAL DAY TO SECOND,则将条件改为:
EXECUTION_ENDED_DATE - EXECUTION_START_DATE > 40/1440
其中“神奇”数字 1440 是一天中的分钟数 (24 * 60)。
您能否详细说明为什么它会阻止 oracle 使用索引。
看一个简单的例子。首先让我们创建一个填充随机数据的测试数据:
CREATE TABLE IW_MASTER_LOG AS
SELECT sysdate - 500*dbms_random.value as EXECUTION_START_DATE,
t.*
FROM all_objects t;
SELECT count(*) FROM IW_MASTER_LOG;
COUNT(*)
----------
74130
接下来在EXECUTION_START_DATE 列上创建索引:
CREATE INDEX my_execution_index ON IW_MASTER_LOG( EXECUTION_START_DATE );
最后刷新表和索引统计:
exec DBMS_STATS.gather_table_stats( user, 'IW_MASTER_LOG' );
现在检查这个查询的执行计划:
EXPLAIN PLAN FOR
SELECT * FROM IW_MASTER_LOG
WHERE EXECUTION_START_DATE >= trunc( sysdate ) - 1
AND EXECUTION_START_DATE < trunc( sysdate );
SELECT * FROM table( DBMS_XPLAN.DISPLAY );
Plan hash value: 3519959109
-----------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 149 | 18476 | 152 (0)| 00:00:01 |
|* 1 | FILTER | | | | | |
| 2 | TABLE ACCESS BY INDEX ROWID BATCHED| IW_MASTER_LOG | 149 | 18476 | 152 (0)| 00:00:01 |
|* 3 | INDEX RANGE SCAN | MY_EXECUTION_INDEX | 149 | | 2 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter(TRUNC(SYSDATE@!)>TRUNC(SYSDATE@!)-1)
3 - access("EXECUTION_START_DATE">=TRUNC(SYSDATE@!)-1 AND
"EXECUTION_START_DATE"<TRUNC(SYSDATE@!))
以上查询使用索引
现在为使用您的条件的查询生成一个计划:
EXPLAIN PLAN FOR
SELECT * FROM IW_MASTER_LOG
WHERE trunc(EXECUTION_START_DATE) = trunc( sysdate ) ;
SELECT * FROM table( DBMS_XPLAN.DISPLAY );
Plan hash value: 3290956462
-----------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 741 | 91884 | 378 (1)| 00:00:01 |
|* 1 | TABLE ACCESS FULL| IW_MASTER_LOG | 741 | 91884 | 378 (1)| 00:00:01 |
-----------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter(TRUNC(INTERNAL_FUNCTION("EXECUTION_START_DATE"))=TRUNC(SYSDAT
E@!))
如你所见,查询执行全表扫描 - trunct 函数阻止 Oracle 使用该索引。
正如@Анатолий Предеин 所建议的:
您可以为此列的函数创建索引,例如创建索引
id_trunc_esd on IW_MASTER_LOG(TRUNC(EXECUTION_START_DATE))
他是绝对正确的,让我们来看看他的建议:
CREATE INDEX another_index ON IW_MASTER_LOG( TRUNC(EXECUTION_START_DATE) );
exec DBMS_STATS.gather_table_stats( user, 'IW_MASTER_LOG' );
EXPLAIN PLAN FOR
SELECT * FROM IW_MASTER_LOG
WHERE trunc(EXECUTION_START_DATE) = trunc( sysdate ) ;
SELECT * FROM table( DBMS_XPLAN.DISPLAY );
Plan hash value: 1627571743
-----------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 148 | 19536 | 142 (0)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID BATCHED| IW_MASTER_LOG | 148 | 19536 | 142 (0)| 00:00:01 |
|* 2 | INDEX RANGE SCAN | ANOTHER_INDEX | 148 | | 1 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access(TRUNC(INTERNAL_FUNCTION("EXECUTION_START_DATE"))=TRUNC(SYSDATE@!))
完美 - 查询使用新索引。您可以听从 Анатолий Предеин 的建议
但是这个索引是有限的——它只能提供具有trunc(EXECUTION_START_DATE) = ... 条件的查询。例如,它不能用于选择某个日期和该日期 + 10 分钟之间的行 - 我们仍然需要在 EXECUTION_START_DATE 列上的普通索引。如果我问我的 DBA 是否可以创建两个索引而不是只创建一个索引,因为我太懒了,在查询中重写了一个条件 trunc( date ),那么他可能会打电话给我。