【问题标题】:Convert seconds value to the ISO 8601 format for duration in Oracle SQL在 Oracle SQL 中将秒值转换为 ISO 8601 格式的持续时间
【发布时间】:2025-12-04 14:10:01
【问题描述】:

我希望将包含秒数(音乐持续时间)的 Oracle 数据库列转换为 ISO 8601 格式的持续时间。但是我到处搜索了没有成功的 Oracle 过程或 SQL 片段。我看到有其他语言的示例,但没有 Oracle SQL 或 PL/SQL。

这是the ISO standard的一些信息。

我使用的是 Oracle 11g 数据库。

【问题讨论】:

  • 你能给出一个样本持续时间值和你期望的结果吗?以及您期望的范围 - 例如,持续时间可以超过一个小时吗?
  • 是的,许多贝多芬或马扎特的作品时长超过 1 小时。
  • 对于通过搜索到达这里的任何人,对于更新的 Oracle 版本,我们可以使用 JSON 来获取 ISO 持续时间:SELECT json_value(json_array(numtodsinterval(123456.789, 'second')), '$[0]' ) 来自对偶;返回“P1DT10H17M36.789000S”

标签: sql oracle iso


【解决方案1】:

你也可以使用EXTRACT函数:

WITH t AS (
  SELECT INTERVAL '59' SECOND AS duration FROM dual
  UNION ALL SELECT INTERVAL '60' SECOND FROM dual
  UNION ALL SELECT INTERVAL '61' SECOND FROM dual
  UNION ALL SELECT INTERVAL '3599' SECOND FROM dual
  UNION ALL SELECT INTERVAL '3600' SECOND FROM dual
  UNION ALL SELECT INTERVAL '86399' SECOND FROM dual
)
SELECT duration,
    'PT'
        ||NULLIF(EXTRACT(HOUR FROM duration)||'H', '0H') 
        ||NULLIF(EXTRACT(MINUTE FROM duration)||'M', '0M') 
        ||NULLIF(EXTRACT(SECOND FROM duration)||'S', '0S')  
    AS iso_value
FROM t;

【讨论】:

  • 可能值得一提的是,他们需要执行 numtodsinterval(duration, 'SECOND') 才能将原始秒数转换为间隔。
  • INTERVAL '59' SECOND AS duration 返回一个INTERVAL 数据类型。
  • 当然,但是 OP 以持续时间列中的数字开头,而不是间隔。
【解决方案2】:

Oracle 有一个区间数据类型,您可以将持续时间转换成它,但您不能真正格式化它。最简单的方法是使用 mod 和 trunc 将值分解为完整的小时和分钟,以及剩余的秒数。

一些样本数据:

with t (duration) as (
  select 59 from dual
  union all select 60 from dual
  union all select 61 from dual
  union all select 3599 from dual
  union all select 3600 from dual
  union all select 86399 from dual
)
select duration,
  numtodsinterval(duration, 'SECOND') as interval_value,
  'PT'
    || case when trunc(duration/3600) > 0 then trunc(duration/3600) || 'H' end
    || case when trunc(duration/3600) > 0 or trunc(mod(duration, 3600)/60) > 0
        then trunc(mod(duration, 3600)/60) || 'M' end
    || trunc(mod(duration, 60)) || 'S' as iso_value
from t;

  DURATION INTERVAL_VAL ISO_VALUE  
---------- ------------ ------------
        59 0 0:0:59.0   PT59S       
        60 0 0:1:0.0    PT1M0S      
        61 0 0:1:1.0    PT1M1S      
      3599 0 0:59:59.0  PT59M59S    
      3600 0 1:0:0.0    PT1H0M0S    
     86399 0 23:59:59.0 PT23H59M59S 

我假设持续时间永远不会超过 24 小时,并且您只需要整秒;但如有必要,可以处理更长的持续时间和更高的精度。

我也猜你想排除最高的可选部分,所以你不会显示 0H 说;但是显示小时后,即使分钟和秒为零,您也想显示分钟和秒。如果没有,您可以调整(或删除)case 语句。

始终显示所有三个部分:

select duration,
  numtodsinterval(duration, 'SECOND') as interval_value,
  'PT' || trunc(duration/3600) || 'H'
    || trunc(mod(duration, 3600)/60) || 'M'
    || trunc(mod(duration, 60)) || 'S' as iso_value
from t;

  DURATION INTERVAL_VAL ISO_VALUE  
---------- ------------ ------------
        59 0 0:0:59.0   PT0H0M59S   
        60 0 0:1:0.0    PT0H1M0S    
        61 0 0:1:1.0    PT0H1M1S    
      3599 0 0:59:59.0  PT0H59M59S  
      3600 0 1:0:0.0    PT1H0M0S    
     86399 0 23:59:59.0 PT23H59M59S 

从不显示零部分:

select duration,
  numtodsinterval(duration, 'SECOND') as interval_value,
  'PT'
    || case when trunc(duration/3600) > 0 then trunc(duration/3600) || 'H' end
    || case when trunc(mod(duration, 3600)/60) > 0 then trunc(mod(duration, 3600)/60) || 'M' end
    || case when trunc(mod(duration, 60)) > 0 then trunc(mod(duration, 60)) || 'S' end
    as iso_value
from t;

  DURATION INTERVAL_VAL ISO_VALUE  
---------- ------------ ------------
        59 0 0:0:59.0   PT59S       
        60 0 0:1:0.0    PT1M        
        61 0 0:1:1.0    PT1M1S      
      3599 0 0:59:59.0  PT59M59S    
      3600 0 1:0:0.0    PT1H        
     86399 0 23:59:59.0 PT23H59M59S 

【讨论】:

  • Alex Poole 先生又做了一次,这太棒了,我选择了从不显示零的部分,并且像梦一样工作。谢谢 :-) 你让我开心。
  • @ShaunKinnair - Wernfried 的提取方法获得与我上次查询相同的输出,没有零元素,并且更简洁 - 至少在将数值转换为间隔后。我刚刚意识到,两者都不像您链接到的文章所说的那样处理零持续时间;我的第二个查询会,但对于其他任何你需要处理的查询。