【问题标题】:Oracle SQL To_Char(interval) is not using my format string?Oracle SQL To_Char(interval) 没有使用我的格式字符串?
【发布时间】:2021-09-25 01:02:21
【问题描述】:

在下面的 SQL 中,我想以 HH:MM:SS 格式打印一个间隔(实际上是几秒)。我将秒数转换为间隔,然后尝试使用 TO_CHAR 日期格式。 Oracle 服务器忽略了我的模式并打印了一个完整的时间戳,日期和微秒中的字符为零。我做错了什么?

CASE WHEN (TR.RUN_LENGTH > 0) THEN TO_CHAR(NUMTODSINTERVAL(TR.RUN_LENGTH,'second'), 'HH24:MI:SS')
ELSE '0' END AS RUN_LENGTH,

我得到:+000000000 00:03:22.000000000

【问题讨论】:

  • 你必须使用SUBSTRREGEXP_SUBSTRING
  • 超过 24 小时的间隔你想得到什么?
  • 我正要建议首先转换为interval day(2) to second(0),然后使用substr() 来获取第5个字符 - 但是我看到现在在 21c 中失败了 ORA-25137: Data value out范围.
  • 这是以前开发人员的方法,对他们很有效。我只是将它暴露在更大范围的时间间隔中(我有一个工作运行了好几天),但它失败了,导致我进行了这个练习。

标签: sql oracle


【解决方案1】:

正如您所观察到的,to_char() 在间隔上不起作用。所以,不要考虑间隔。相反,您有几秒钟的时间,并且想要转换为 HH:MM:SS 格式的字符串。

使用算术和字符串运算:

select lpad(floor(tr.run_length / 3600), 2, '0') || ':' || lpad(mod(floor(tr.run_length / 60),  60), 2, '0') || ':' || lpad(mod(tr.run_length, 60), 2, '0') as hhmmss
from (select 1 as run_length from dual union all
      select 10 as run_length from dual union all
      select 100 as run_length from dual union all
      select 1000 as run_length from dual union all
      select 10000 as run_length from dual union all
      select 100000 as run_length from dual 
     ) tr;

Here 是一个 dbfiddle。

【讨论】:

    【解决方案2】:

    我搜索了其他相关回复;很明显,Oracle 实际上并没有为 TO_CHAR(DSINTERVAL) 实现用户指定的格式。正如一些人建议的那样,您可以添加一个日期,然后使用日期格式。但如果间隔超过 24 小时,则会失败。

    我最终使用了 REGEXP_SUBSTRING() 但后来遇到了 Oracle 显然不支持非捕获组的问题。我能够拼凑出这个:

    REGEXP_SUBSTR(TO_CHAR(NUMTODSINTERVAL(TR.RUN_LENGTH,'second')), '^([\+0:\ ]*)(.*)(\.)',1,1,NULL,2 )
    

    这似乎工作得很好。它会抑制任何前导零和小数秒。

    【讨论】:

    • 我建议表达'\d{2}:\d{2}:\d{2}'
    【解决方案3】:

    如果您的时间间隔始终小于 24 小时,那么您可以使用以下方法之一:

    TO_CHAR(TRUNC(SYSDATE) + NUMTODSINTERVAL(TR.RUN_LENGTH, 'second'), 'hh24:mi:ss')
    
    REGEXP_SUBSTR(NUMTODSINTERVAL(TR.RUN_LENGTH,'second'), '\d{2}:\d{2}:\d{2}')
    

    否则需要使用EXTRACT函数:

    SELECT
        TO_CHAR(
           EXTRACT(DAY FROM NUMTODSINTERVAL(TR.RUN_LENGTH,'second')) * 24 
           + EXTRACT(HOUR FROM NUMTODSINTERVAL(TR.RUN_LENGTH,'second')) )
       || ':' EXTRACT(MINUTE FROM NUMTODSINTERVAL(TR.RUN_LENGTH,'second') 
       || ':' TRUNC(EXTRACT(SECOND FROM NUMTODSINTERVAL(TR.RUN_LENGTH,'second'))
    

    如果您的输出需要,您必须在部件周围添加LPAD(..., 2, '0')(可选)。

    【讨论】:

      【解决方案4】:

      试试这个:

      TO_CHAR (TRUNC (SYSDATE) + NUMTODSINTERVAL ((TR.RUN_LENGTH, 'second'),   
          'hh24:mi:ss' )
      

      NUMTODSINTERVAL 的文档说它返回一个字符串文字,因此 to_char 无效。上面的这个 hack 只需要当前日期/时间(sysdate),截断 HH:mm:ss 组件,然后添加回您指定的 #seconds,然后将其格式化为仅显示 HH:MM:SS 值

      【讨论】:

      • 哪个文档?该函数返回一个区间,而不是一个字符串。
      • 但是这个 hack 只适用于少于 24 小时的间隔。
      • @mathguy,这是我假设的,但文档说,“NUMTODSINTERVAL 将 n 转换为 INTERVAL DAY TO SECOND 文字。”调用,从 dual 中选择长度(NUMTODSINTERVAL(100,'second'));返回 29,然后是返回值的长度。
      • @oldprogrammer 因为length 将字符表达式作为输入,所以interval datatype 被转换为它的字符串表示。 numtodsinterval 的文档有一个误导性的描述,因为它不返回任何文字,而是一个内置数据类型的值
      猜你喜欢
      • 2018-12-24
      • 2015-09-05
      • 2018-05-31
      • 1970-01-01
      • 1970-01-01
      • 2019-11-25
      • 2013-03-24
      • 2020-11-03
      • 2014-10-22
      相关资源
      最近更新 更多