比较SQL中的时钟时间(Oracle)

比较SQL中的时钟时间(Oracle),sql,oracle,date,time,Sql,Oracle,Date,Time,我目前正在处理Oracle数据库中的一个表,其中有以AM/PM格式存储的时间值。默认情况下,时间是一个VARCHAR2数据类型,因此我一直在使用to_date函数转换它们 我所做的是比较表中的两个不同时间,并运行一个查询,该查询将返回该时间间隔内包含的所有时间的结果。间隔是通过使用between条件获得的。以下是一个例子: SELECT TIME FROM TIME_TABLE WHERE to_date(TIME,'HH:MI:SS PM') between to_dat

我目前正在处理Oracle数据库中的一个表,其中有以AM/PM格式存储的时间值。默认情况下,时间是一个VARCHAR2数据类型,因此我一直在使用to_date函数转换它们

我所做的是比较表中的两个不同时间,并运行一个查询,该查询将返回该时间间隔内包含的所有时间的结果。间隔是通过使用between条件获得的。以下是一个例子:

SELECT TIME
FROM   TIME_TABLE
WHERE  to_date(TIME,'HH:MI:SS PM') between 
       to_date('04:00:00 PM', 'HH:MI:SS PM') and 
       to_date('08:00:00 PM', 'HH:MI:SS PM');
问题是,如果您不使用to_date函数声明日期,它将自动默认为当前月份的1号。因此,如果我想得到从晚上11:59到凌晨12:00的所有结果,我会得到错误的结果


基本上,我想以一种循环的方式来计算时间间隔,而不是像自然的那样只朝一个方向走。有办法做到这一点吗?

难道你不能将am/pm varchar转换为24小时varchar,并将其与
之间的
进行比较吗

SELECT TIME 
  FROM TIME_TABLE
 WHERE to_char(to_date(TIME,'HH:MI:SS PM'),'HH24:MI:SS') 
         BETWEEN '04:00:00' AND '05:00:00';

您不能将am/pm varchar转换为24小时varchar,并将其与
之间的
进行比较吗

SELECT TIME 
  FROM TIME_TABLE
 WHERE to_char(to_date(TIME,'HH:MI:SS PM'),'HH24:MI:SS') 
         BETWEEN '04:00:00' AND '05:00:00';

这里最大的问题是,随着数据的增加,速度会越来越慢。在对字段本身调用函数时,很难有效地使用索引。它必须扫描整个表才能满足查询。有三种方法可以避免这种情况:

  • 不要将日期或时间存储为字符串。使用正确的字段类型。这是最好的办法

  • 使用字符串进行比较,而不是先转换。只有在保证字典顺序的情况下,这才有效。你可以在24小时内完成这项工作,但不能在12小时内完成,因为AM/PM会妨碍你

  • 正如巴雷特在评论中告诉我的那样,甲骨文的概念是。您可以创建一个预计算截至日期的
    结果的函数,这样当您在查询中使用它时,它就有了一个索引。虽然有几个,所以如果你采用这种方法,我建议你仔细考虑后果。

关于在午夜仅时间值上循环的问题,一般算法如下:

  • 是范围的开始值=范围的开始和范围的结束。否则返回false

  • 如果否,则如果测试值>=范围的开始<范围的结束,则返回true。否则返回false


不需要使用:59或任何其他类型的值。这样做可能会妨碍其他事情,例如测量范围的持续时间或测试一个值,如:59.5。

这里最大的问题是,随着添加更多数据,事情会变得越来越慢。在对字段本身调用函数时,很难有效地使用索引。它必须扫描整个表才能满足查询。有三种方法可以避免这种情况:

  • 不要将日期或时间存储为字符串。使用正确的字段类型。这是最好的办法

  • 使用字符串进行比较,而不是先转换。只有在保证字典顺序的情况下,这才有效。你可以在24小时内完成这项工作,但不能在12小时内完成,因为AM/PM会妨碍你

  • 正如巴雷特在评论中告诉我的那样,甲骨文的概念是。您可以创建一个预计算截至日期的
    结果的函数,这样当您在查询中使用它时,它就有了一个索引。虽然有几个,所以如果你采用这种方法,我建议你仔细考虑后果。

关于在午夜仅时间值上循环的问题,一般算法如下:

  • 是范围的开始值=范围的开始和范围的结束。否则返回false

  • 如果否,则如果测试值>=范围的开始<范围的结束,则返回true。否则返回false


不需要使用:59或任何其他类型的值。这样做可能会妨碍其他事情,例如测量范围的持续时间或测试一个值,如:59.5。

不太可能。您需要添加一些逻辑来检测中间条件中的“循环”。如果存在循环(endRange
    to_char(to_date(TIME,'HH:MI:SS PM'),'HH24:MI:SS') >= '08:00:00' // endRange
 OR to_char(to_date(TIME,'HH:MI:SS PM'),'HH24:MI:SS') <= '04:00:00' // startRange
to_char(to_date(TIME,'HH:MI:SS PM'),'HH24:MI:SS')>='08:00:00'//endRange

或者to_char(to_date(时间,'HH:MI:SS PM'),'HH24:MI:SS')不太可能。您需要添加一些逻辑来检测中间条件中的“循环”。如果存在循环(endRange
    to_char(to_date(TIME,'HH:MI:SS PM'),'HH24:MI:SS') >= '08:00:00' // endRange
 OR to_char(to_date(TIME,'HH:MI:SS PM'),'HH24:MI:SS') <= '04:00:00' // startRange
to_char(to_date(TIME,'HH:MI:SS PM'),'HH24:MI:SS')>='08:00:00'//endRange

或者to_char(to_date(TIME,'HH:MI:SS PM'),'HH24:MI:SS')您可以测试第二次是否比第一次“早”,如果是,则将其调整到第二天。您需要以同样的方式调整表时间。使用CTE只是为了不必重复字符串值和转换:

WITH R AS (
  SELECT to_date('11:59:00 PM', 'HH:MI:SS PM') start_t,
         to_date('12:00:00 AM', 'HH:MI:SS PM') end_t
  FROM   DUAL
),
T AS (
  SELECT to_date(TIME,'HH:MI:SS PM') AS TIME
  FROM TIME_TABLE
)
SELECT to_char(TIME, 'HH:MI:SS PM')
FROM   R
JOIN   T
ON     T.time + case when T.time < R.start_t then 1 else 0 end
       between R.start_t and 
         R.end_t + case when R.end_t < R.start_t then 1 else 0 end
ORDER BY T.time + case when T.time < R.start_t then 1 else 0 end;
,其中R为(
选择截止日期('11:59:00 PM','HH:MI:SS PM')开始,
截止日期('12:00:00 AM','HH:MI:SS PM')结束
来自双重
),
塔斯(
选择日期(时间,'HH:MI:SS PM')作为时间
从时间表
)
选择以显示字符(时间'HH:MI:SS PM')
从R
加入T
在T.time+情况下,当T.time
这不是很愉快

希望这是一张小桌子,你不会看到马特·约翰逊提到的性能问题

您可以通过生成范围内所有可能的时间并将这些时间与实际表作为字符串进行比较来避免这种情况:

WITH R AS (
  SELECT to_date('11:10:00 PM', 'HH:MI:SS PM') start_t,
         to_date('12:30:00 AM', 'HH:MI:SS PM') end_t
  FROM   DUAL
),
T AS (
  SELECT level AS rn,
         to_char(start_t
             + numtodsinterval(level - 1, 'SECOND'),
           'HH:MI:SS PM') AS time
  FROM R
  CONNECT BY level <= 86400 *
    (end_t + case when end_t < start_t then 1 else 0 end - start_t)
)
SELECT TT.time
FROM   T
JOIN   TIME_TABLE TT
ON     TT.time = T.time
ORDER BY T.rn;
W