ORA-01722: 尝试在接下来 30 秒内获取预定信号的 Oracle 查询中的无效数字

Posted

技术标签:

【中文标题】ORA-01722: 尝试在接下来 30 秒内获取预定信号的 Oracle 查询中的无效数字【英文标题】:ORA-01722: invalid number in an Oracle query that tries to pick up the scheduled signals in the next 30 seconds 【发布时间】:2013-09-05 09:19:12 【问题描述】:

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

signal_type VarChar(2) signal_time 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 子句中时,任何人都可以解释为什么会出现上述错误吗?

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

【问题讨论】:

1.您无法将 15:35:30 存储在给定的数据类型 varchar(2) 中。 2.您的第一个选择查询似乎没有任何错误。 3.您的选择查询有额外的逗号错误 你的逻辑会在 00:00:15 的信号时间工作吗? 【参考方案1】:

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

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

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')
;

Yet another fiddle。这会将时间部分提取为一天的一小部分,对于 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')
;

Another fiddle 显示此工作,然后较早的更简单的查询失败,使用相同的数据 - 包括一条错误记录。

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

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

【讨论】:

感谢亚历克斯的回复。实际上,真正的罪魁祸首可能是不合格的记录。在我编写此查询的真实表中,有 2 条记录,其中 signal_time 值的格式不同。我试过这个 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') 我也尝试了类似下面的 pure_signal as( select signal_type, signal_time from signal_table where signal_type != 'AAA' and signal_type != 'BBB' ) select * from pure_signal 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') 对于上面的查询,我得到“ORA-01830:日期格式图片在转换整个输入字符串之前结束”不幸的是,我没有权限在生产中创建临时表... 抱歉,这些 cmets 看起来很混乱...我希望我能以与原始帖子相同的方式对其进行格式化... @akisoni - 这两条记录是否合法地具有不同的格式(肯定不是?),还是可以修复?他们的数据是否必须包含在查询中,如果是,它们是什么格式?【参考方案2】:
SELECT
TO_CHAR(TO_DATE(signal_time, 'HH24:MI:SS'),  'HH24:MI:SS') - TO_CHAR(NOW(),  'HH24:MI:SS')
;

【讨论】:

Oracle 中没有now() 函数。 now() 不是 Oracle 函数,您需要使用 sysdate。更重要的是,从另一个字符串中减去一个字符串会给您带来什么? Oracle 会再次抱怨 ORA-01722,因为它会尝试将两个字符串隐式转换为数字,这会失败。 感谢您的建议。我实际上也尝试将 signal_time 转换为日期格式,但得到“ORA-01830:日期格式图片在转换整个输入字符串之前结束”。当我作为 cmets 发布到 Alex 的另一个答案线程时,这可能是由于表中实际上有一些不符合格式的记录。到目前为止,我一直无法解决这个问题......

以上是关于ORA-01722: 尝试在接下来 30 秒内获取预定信号的 Oracle 查询中的无效数字的主要内容,如果未能解决你的问题,请参考以下文章

ORA-01722: 无效编号 当我尝试执行存储过程时。甲骨文错误

仅在案例语句中添加年份得到无效数字错误 - ORA-01722

ORA-01722 错误消息

创建交互式报表时的 Oracle APEX ORA-01722

ORA-01722 运行存储过程时出错

SQL ORA-01722: 无效号码