【问题标题】:Oracle SQL Date RangesOracle SQL 日期范围
【发布时间】:2016-10-25 13:58:01
【问题描述】:

我有以下疑问:

select * from sa_tran_head WHERE
((TRAN_DATETIME) BETWEEN NVL(:STARTDATE, TRAN_DATETIME) AND NVL(:ENDDATE, TRAN_DATETIME));

如果我输入STARTDATE = 21-JUN-2016 and ENDDATE = 21-JUN-2016,它不会显示任何内容。 当我输入STARTDATE = 21-JUN-2016 and ENDDATE = 22-JUN-2016 时,它显示了 21-JUN-2016 的结果。

为什么会这样?

【问题讨论】:

    标签: sql oracle date oracle11g


    【解决方案1】:

    我的猜测? TRAN_DATETIME也存储时间值?

    使用TRUNC()

    select * from sa_tran_head
    WHERE TRUNC(TRAN_DATETIME) BETWEEN NVL(:STARTDATE, TRUNC(TRAN_DATETIME)) 
                                   AND NVL(:ENDDATE, TRUNC(TRAN_DATETIME));
    

    【讨论】:

    • 使用TRUNC( TRAN_DATETIME ) 意味着该列上的任何索引都不能使用。要使用索引,您需要一个基于函数的索引。
    • 要使用索引,他可以使用他当前的查询,范围在昨天和今天之间或@MT0
    【解决方案2】:

    日期时间在内部被视为正确的时间戳。当您提到日期为21-Jun-2016 时,它被视为21-Jun-2016 00:00:00.000。因此,它在第一个条件中检查 21-Jun-2016 00:00:00.00021-Jun-2016 00:00:00.000 中的值,解释你得到的输出。

    【讨论】:

    • Oracle 没有datetime 数据类型。它有DATETIMESTAMP,它们都有一个时间组件。 OP 正在谈论DATEs,这些不是以毫秒精度存储的。
    【解决方案3】:

    可以忽略小时、分钟和秒吗? 看例子:

    create table t(v date)
    /
    insert into t(v) values(to_date('2016-06-23 23:00:00', 'yyyy-mm-dd hh24:mi:ss'))
    /
    -- no rows
    select * from t where
    v BETWEEN Nvl(to_date('2016-06-23 08:00:00', 'yyyy-mm-dd hh24:mi:ss'), v) 
        AND NVL(to_date('2016-06-23 08:00:00', 'yyyy-mm-dd hh24:mi:ss'), v);
    /
    -- exactly one
    select * from t where
    v BETWEEN Nvl(to_date('2016-06-23 08:00:00', 'yyyy-mm-dd hh24:mi:ss'), v) 
        AND NVL(to_date('2016-06-24 08:00:00', 'yyyy-mm-dd hh24:mi:ss'), v);
    /
    

    【讨论】:

      【解决方案4】:

      Oracle 将日期存储在7- or 8-byte data structure 中,总是包含时间组件。

      当您有一个日期TO_DATE( '21-JUN-2016', 'DD-MON-YYYY' ) 时,Oracle 将创建带有时间00:00:00 的日期。

      如果你这样做:

      WHERE TRAN_DATETIME BETWEEN TO_DATE( '21-JUN-2016 00:00:00', 'DD-MON-YYYY HH24:MI:SS' )
                              AND TO_DATE( '21-JUN-2016 00:00:00', 'DD-MON-YYYY HH24:MI:SS' )
      

      那么你只会得到你想要的结果并且有时间组件00:00:00

      现在您可以使用TRUNC() 将时间组件设置为00:00:00

      WHERE TRUNC( TRAN_DATETIME ) BETWEEN DATE '2016-06-21' AND DATE '2016-06-21'
      

      但是,如果 TRAN_DATETIME 列上有索引,则此查询将不会使用它(但是,它可以在 TRUNC( TRAN_DATETIME ) 上使用基于函数的索引)。

      可以使用索引的解决方案是:

      WHERE TRAN_DATETIME >= :start_date 
      AND   TRAN_DATE     <  :end_date + INTERVAL '1' DAY
      

      (假设 :start_date:end_date 是传入的绑定变量,其时间分量为 00:00:00 - 这就是您正在做的事情)。

      合并NVL 语句:

      WHERE ( :start_date IS NULL OR TRAN_DATETIME >= :start_date )
      AND   ( :end_date   IS NULL OR TRAN_DATE     <  :end_date + INTERVAL '1' DAY )
      

      【讨论】:

        猜你喜欢
        • 2011-03-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-12-31
        • 2022-01-13
        • 2020-09-26
        • 1970-01-01
        相关资源
        最近更新 更多