【问题标题】:How to select records which has difference between two dates more than 40 minutes? in Oracle Preferably如何选择两个日期相差超过 40 分钟的记录?在 Oracle 中最好
【发布时间】:2016-07-15 05:09:37
【问题描述】:

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

select * from iwdata.IW_MASTER_LOG
WHERE TRUNC(EXECUTION_START_DATE)=TRUNC(SYSDATE)  
AND (EXECUTION_START_DATE-EXECUTION_ENDED_DATE) >to_date('40','mi');

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

ORA-00932:不一致的数据类型:预期 NUMBER 得到 DATE

【问题讨论】:

    标签: sql oracle date


    【解决方案1】:

    使用

    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 ),那么他可能会打电话给我。

    【讨论】:

    • 您好 krokodilko,您能否详细说明为什么它会阻止 oracle 使用索引。另外,当我尝试间隔“40”分钟时。它给出了一个错误“ORA-00932:不一致的数据类型:预期的 NUMBER 得到了 INTERVAL DAY TO SECOND”
    • krokodilko 尝试说,如果您在查询中使用 TRUNC(EXECUTION_START_DATE) 怎么办?它不使用 EXECUTION_START_DATE 列上的索引,因为它起作用,您可以为该列创建函数索引,如 create index id_trunc_esd on IW_MASTER_LOG(TRUNC(EXECUTION_START_DATE))
    • 感谢您的回复。 :)
    【解决方案2】:

    这样试试

    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;
    

    【讨论】:

    • 嗨 Анатолий Предеин,感谢您的回复。查询给我带来了想要的结果。您能否详细说明您是如何处理会议记录的。
    • 是不是像 .. 日已转换为小时和分钟?
    • 当你的日期减去日期时,这个回报是这两个日期之间的天数,这就是为什么,我将值乘以 24 得到小时,然后乘以 60 得到分钟。
    • @Arjun Raja 我已经更新了答案,请看一下。
    • 感谢您的回复:)
    【解决方案3】:
    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 分钟)

    【讨论】:

    • 嗨,彼得,感谢您提供的信息。但是,查询似乎没有获取预期的结果。我尝试运行该查询,它只返回 2 条记录。但我相信还有很多其他超过 40 分钟的记录。
    • 嗨,Arjun,我已经更新了我的查询 - 以前我忘了只过滤今天的记录。现在开始日期必须是今天。如果该过程在第二天运行并结束,那没关系。你也会得到结果。不幸的是,我不知道为什么您检索的行数比您认为的要少,它应该可以工作。
    • 感谢您的回复:)
    猜你喜欢
    • 1970-01-01
    • 2018-09-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-10-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多