【问题标题】:Compare timestamps in Oracle比较 Oracle 中的时间戳
【发布时间】:2017-06-24 13:29:19
【问题描述】:

我有一列用于在 Oracle 11g Express 的数据库中的 VARCHAR2 列中存储日期和时间数据,格式如下:

2016 年 9 月 30 日 14:00:00

我正在尝试获取不同时间范围内的数据的方法。我找到了以下两种方式:

select * 
from dummy
WHERE starttime > '9/30/2016 14:00:00' 
AND starttime < '9/30/2016 17:00:00' 
order by starttime;

select * 
from dummy 
WHERE to_timestamp(starttime, 'mm/dd/yyyy hh24:mi:ss') > TO_TIMESTAMP('9/30/2016 14:00:00', 'mm/dd/yyyy hh24:mi:ss') 
AND TO_TIMESTAMP(starttime, 'mm/dd/yyyy hh24:mi:ss') < TO_TIMESTAMP('9/30/2016 17:00:00', 'mm/dd/yyyy hh24:mi:ss');

我想知道第一种方法是如何工作的,因为列 starttime 以 VARCHAR 格式存储,并且在不转换为时间戳的情况下,比较仍然有效。有人可以向我解释这是如何/为什么会发生的,或者是否有一些极端情况不起作用?谢谢。

【问题讨论】:

  • 第一种方法也是如何工作的?它没有。它可能不会崩溃,但不会返回有意义的结果。如果您的格式类似于YYYY-MM-DD,则按字母顺序仅适用于日期。
  • 是的,我知道它不起作用。但是对于上述特殊情况,它起作用了。所以我只是想了解它在特定情况下是如何工作的。
  • '9/30/2016 14:00:00' - '9/30/2016 17:00:00' 可能有效,但其他范围(例如'9/30/2016 20:00:00' - '12/1/2016 17:00:00')将失败。转换为TIMESTAMPDATE)是正确的方法。
  • 为什么字母排序在“一、二”中起作用,而在“四、五”中不起作用?纯属偶然。

标签: sql oracle timestamp


【解决方案1】:

将日期存储为 varchar 不太聪明......

您的第一种方法很好,只要您不需要跨越一年的界限。这些数字从左到右进行比较(因为是文本)。除非您存储为 'YYYY-MM-DD HH24:MI:SS',否则您会遇到问题。

2 个选项,将存储更改为 DATE,或在 WHERE 子句中使用 to_dateto_timestamp(我推荐 to_date

【讨论】:

    【解决方案2】:

    这很有效,因为您对月份和年份或一位数与两位数的天数没有任何问题。想想在 '9/30/2016 14:00:00' 到 '9/30/2016 17:00:00' 范围内的任何字符串。他们都必须从 '9/30/2016 1' 开始。

    如果从“2016 年 9 月 30 日 14:00:00”到“2016 年 10 月 30 日 17:00:00”的范围内,您根本找不到任何记录,因为字符串会包含从 >= '9' 和

    所以是特定日期内的狭窄范围将您救到了这里:-)

    【讨论】:

    • 我在这里与 JohnHC 在一起:正确 方法根本不是将日期时间存储为字符串,而是存储为日期时间(在 Oracle 中键入 DATE 或 TIMESTAMP)。但是,如果由于某种原因您必须处理存储为字符串的日期时间,那么可以,第二种转换为日期时间的方法是可行的方法。 (我可能不会在每个查询中都这样做,而是编写一个视图然后使用我猜。)
    【解决方案3】:

    将日期/时间存储为字符串或数字是个坏主意。优化器不知道域,因此在尝试估计基数时,您并没有给优化器最好的机会。例如,考虑以下两个日期

    Dec 31st 2016
    Jan 1st 2017
    

    如果将这些存储为数字,则可以使用

    20170101 and 20161231
    

    那么他们之间的天数是多少?使用数字,你得到

    20170101 - 20161231
    = 8870
    

    但是,真正的(基于日期的)答案是 1。

    虽然您可以 TO_DATE()CAST 您的列,但您现在面临无法使用某些优化的风险,例如索引、分区修剪、布隆过滤等。

    所以我强烈建议使用正确的数据类型。

    【讨论】:

      【解决方案4】:

      将值存储在VARCHAR 列中意味着您将进行字符串比较:

      SELECT * 
      FROM   dummy
      WHERE  starttime > '9/30/2016 14:00:00' 
      AND    starttime < '10/30/2016 17:00:00' 
      ORDER BY starttime;
      

      这将查看开始时间并逐个字符地考虑它,如果第一个字符大于'9' 并且小于'1',那么它将返回一行(因为这永远不会是真的不会返回一行)。此外,它不会认为910 代表月份,并且在进行字符串比较时'09/30/2016' &lt; '09/31/1900' &lt; '10/30/2016'

      即使您将值存储在 TIMESTAMP 列中,使用字符串文字也是一个坏主意:

      SELECT * 
      FROM   dummy
      WHERE  starttime > '9/30/2016 14:00:00' 
      AND    starttime < '9/30/2016 17:00:00' 
      ORDER BY starttime;
      

      这是因为 Oracle 将使用会话参数 NLS_TIMESTAMP_FORMAT 作为格式掩码执行隐式转换(使用 TO_TIMESTAMP( time, format_mask ))。

      因此,您的查询(假设为 TIMESTAMP 数据类型)实际上是(尽管 Oracle 将以更有效的方式实现它):

      SELECT * 
      FROM   dummy
             CROSS JOIN
             ( SELECT value AS format_mask
               FROM NLS_SESSION_PARAMETERS
               WHERE PARAMETER = 'NLS_TIMESTAMP_FORMAT' ) nls
      WHERE  starttime > TO_TIMESTAMP( '9/30/2016 14:00:00', nls.format_mask )
      AND    starttime < TO_TIMESTAMP( '9/30/2016 17:00:00', nls.format_mask )
      ORDER BY starttime;
      

      NLS_TIMESTAMP_FORMAT 是一个会话参数 - 这意味着每个用户都可以在他们自己的会话中为该参数设置自己的值,如果一个用户将其更改为 YYYY-MM-DD"T"HH24:MI:SS.ZZZ"Z"(即 ISO8601 格式),那么您的查询将中断对您的查询没有任何更改的该用户(而不是未更改它的其他用户)。

      与其使用字符串文字和隐式转换,不如显式设置您期望的格式掩码或使用 ANSI TIMESTAMP 文字:

      SELECT * 
      FROM   dummy
      WHERE  TO_TIMESTAMP( starttime, 'MM-DD-YYYY HH24:MI:SS' ) > TIMESTAMP '2016-09-30 14:00:00' 
      AND    TO_TIMESTAMP( starttime, 'MM-DD-YYYY HH24:MI:SS' ) < TIMESTAMP '2016-09-30 17:00:00'
      ORDER BY TO_TIMESTAMP( starttime, 'MM-DD-YYYY HH24:MI:SS' );
      

      然后,您将受益于TO_TIMESTAMP( starttime, 'MM-DD-YYYY HH24:MI:SS' ) 上的基于函数的索引。

      更好的是,将您的列转换为正确的 TIMESTAMP 格式,然后您不需要基于函数的索引,并且可以使用 TIMESTAMP 文字作为边界,而无需任何转换函数。

      【讨论】:

        【解决方案5】:

        在 Oracle 上,您也可以使用:

        SELECT * FROM table 
        WHERE field BETWEEN TRUNC(SYSDATE - 6) AND SYSDATE
        

        【讨论】:

          猜你喜欢
          • 2017-07-10
          • 2012-09-15
          • 2020-08-14
          • 1970-01-01
          • 2012-07-09
          • 1970-01-01
          • 2015-09-22
          • 2018-03-10
          相关资源
          最近更新 更多