0

我在 Oracle 11G 中有一个所谓的 signal_table,其中包含以下列:

信号类型 VarChar(2)
信号时间 VarChar(2)

signal_time 的格式为 HH24:MI:SS(例如“15:35:30”),这意味着应用程序会在每天 15:35:30 触发此类信号。

现在,我正在尝试编写一个查询,列出所有将在 30 秒后触发的信号。

我尝试了类似的东西

select  
  signal_type,  
  signal_time,  
from  
  signal_table  
where  
  to_number(to_char(substr(signal_time,1,2)))*3600 + to_number(to_char(substr(signal_time,4,2)))*60 + to_number(to_char(substr(signal_time,7,2))) - to_number(to_char(sysdate,'HH24'))*3600 + to_number(to_char(sysdate,'MI'))*60 + to_number(to_char(sysdate,'SS')) < 30
;

认为我会以秒为单位将 VarChar 时间戳转换为数字。
但是,对于上述查询,我​​收到“ORA-01722:无效号码”错误。

如下查询可以正常工作:

select  
  signal_type,  
  signal_time,  
  to_number(to_char( substr(signal_time,1,2)))*3600 + to_number(to_char(substr(signal_time,4,2)))*60 + to_number(to_char(substr(signal_time,7,2))) - to_number(to_char(sysdate,'HH24'))*3600 + to_number(to_char(sysdate,'MI'))*60 + to_number(to_char(sysdate,'SS'))
from  
  signal_table  
;

当我将 to_number 演员部分放在 where 子句中时,任何人都可以解释为什么会出现上述错误吗?

更好的是,有没有更优雅的方式来实现这个目标?

4

2 回答 2

0

只要表中的所有数据确实具有nn:nn:nn格式值,您所做的工作(在一定程度上)。该错误表明您至少有一行格式不正确。尽管您的第二个查询有效,这很奇怪-也许您有一个where未显示的子句?

这是一个SQL Fiddle 显示它的动作。它没有错误,但它显示了今天已经过去的所有信号时间,所以如果我理解你的话,你需要将它限制在当前时间和现在的 30 秒之间。这个小提琴做到了这一点,并且还删除了一些不必要的转换:

select  
  signal_type,
  signal_time
from
  signal_table
where  
  (to_number(substr(signal_time,1,2))*3600
    + to_number(substr(signal_time,4,2))*60
    + to_number(substr(signal_time,7,2)))
    - (to_number(to_char(sysdate,'HH24'))*3600
    + to_number(to_char(sysdate,'MI'))*60
    + to_number(to_char(sysdate,'SS'))) >= 0
and
  (to_number(substr(signal_time,1,2))*3600
    + to_number(substr(signal_time,4,2))*60
    + to_number(substr(signal_time,7,2)))
    - (to_number(to_char(sysdate,'HH24'))*3600
    + to_number(to_char(sysdate,'MI'))*60
    + to_number(to_char(sysdate,'SS'))) < 30
;

您还可以将其signal_time转换为日期并将其与 sysdate 进行比较:

select
  signal_type,
  signal_time
from
  signal_table
where
  to_date(signal_time, 'HH24:MI:SS') - trunc(sysdate, 'MM')
    >= sysdate - trunc(sysdate, 'DD')
  and to_date(signal_time, 'HH24:MI:SS') - trunc(sysdate, 'MM')
    < sysdate + interval '30' second - trunc(sysdate, 'DD')
;

又一个小提琴。这会将时间部分提取为一天的一小部分,即signal_time当前时间和当前时间 + 30 秒。

要查找格式不正确的值,快速检查类似于:

select
  signal_type,
  signal_time
from
  signal_table
where
  not regexp_like(signal_time, '^\d\d:\d\d:\d\d$')
;

(我对正则表达式不太感兴趣,所以可能可以做得更整洁)。不过,这只会找到格式错误的值,并且不会发现无效时间,例如24:60:60,您的查询将转换的时间。日期版本会抱怨这一点(ORA-01850),但也会更宽容略有不同的格式,例如缺少一个元素的前导零。这可能是也可能不是一件好事。


如果您有需要排除但无法更正的不同格式的记录,那么(除了建议数据模型问题之外)您可以将检查查询用作主要查询中的子查询:

select
  signal_type,
  signal_time
from
  (
    select
      *
    from
      signal_table
    where
      regexp_like(signal_time, '^\d\d:\d\d:\d\d$')
  )
where
  to_date(signal_time, 'HH24:MI:SS') - trunc(sysdate, 'MM')
    > sysdate - trunc(sysdate, 'DD')
  and to_date(signal_time, 'HH24:MI:SS') - trunc(sysdate, 'MM')
    < sysdate + interval '30' second - trunc(sysdate, 'DD')
;

另一个显示这个工作的小提琴,然后是更早的更简单的查询失败,具有相同的数据 - 包括一个坏记录。

如果您的格式有效但时间无效(例如24:60:60),它仍然会失败。如果是这种情况,那么您确实需要清理数据,但可能会提出更严格的正则表达式,或者使用类似这样的函数在运行时检查有效格式。

在某些情况下,您可能需要添加提示以停止将过滤器应用于内部选择,但我认为在这种情况下这不会成为问题。

于 2013-09-05T10:21:08.907 回答
-1
SELECT
TO_CHAR(TO_DATE(signal_time, 'HH24:MI:SS'),  'HH24:MI:SS') - TO_CHAR(NOW(),  'HH24:MI:SS')
;
于 2013-09-05T09:46:52.657 回答