如何选择两个日期之间差异超过40分钟的记录?在Oracle中最好

时间:2016-07-15 05:09:37

标签: sql oracle date

我必须选择当天的日志记录,执行开始日期和结束日期之间的差异超过40分钟。

/opt/

但是这个查询给了我一个错误。

ORA-00932:数据类型不一致:预期NUMBER为DATE

3 个答案:

答案 0 :(得分:3)

使用

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使用索引。

看一个简单的例子。首先,让我们创建一个充满随机数据的测试dable:

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使用该索引。

As @АнатолийПредеинsogested:

  

您可以为此列创建索引,例如create index   IW_MASTER_LOG上的id_trunc_esd(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 ),那么他可能会打电话给我。

答案 1 :(得分:0)

试试这个

with a as ( select sysdate sd, sysdate+(1/24/60)*39 ed from dual)
-- in with sysdate and sysdate + 39 minute
select to_char(sd,'dd.mm.yyyy hh24:mi:ss'),
       to_char(ed,'dd.mm.yyyy hh24:mi:ss'), 
       to_number(ed-sd) *24 * 60
  from a 
 where to_number(ed-sd) *24 * 60 < 40

在你的情况下

with IW_MASTER_LOG as (select sysdate EXECUTION_START_DATE, sysdate+(1/24/60)*41 EXECUTION_ENDED_DATE from dual  )

select IW_MASTER_LOG.*
  from IW_MASTER_LOG
 WHERE TRUNC(EXECUTION_START_DATE)=TRUNC(SYSDATE)  
   AND to_number(EXECUTION_ENDED_DATE - EXECUTION_START_DATE) *24 * 60 > 40;

答案 2 :(得分:0)

WITH IW_MASTER_LOG AS
  (SELECT SYSDATE AS EXECUTION_START_DATE,
    SYSDATE       AS EXECUTION_END_DATE
  FROM DUAL
  UNION ALL
  SELECT SYSDATE, SYSDATE+1/24/60*30 FROM DUAL
  UNION ALL
  SELECT SYSDATE, SYSDATE+1/24/60*41 FROM DUAL
  )
SELECT *
FROM IW_MASTER_LOG
WHERE (EXECUTION_END_DATE-EXECUTION_START_DATE)>=1/24/60*40
AND TRUNC(EXECUTION_START_DATE)                 = TRUNC(SYSDATE); --started today

Oracle日期表示为天数的分数,意味着什么。

1 - day
1/24 - hour
1/24/60 - minute
1/24/60*40 - 40 mins

TRUNC - 省略第二个参数返回日期。

在以下数据集

的结果中使用 WITH 子句
07.15.2016 14:13:54 | 07.15.2016 14:13:54
07.15.2016 14:13:55 | 07.15.2016 14:43:55
07.15.2016 14:13:56 | 07.15.2016 14:54:56

查询仅返回最后一条记录(所需内容,因为41分钟)