在 Oracle SQL 中将秒值转换为 ISO 8601 格式的持续时间

Convert seconds value to the ISO 8601 format for duration in Oracle SQL

我正在寻找将保存秒数(音乐持续时间)的 Oracle 数据库列转换为 ISO 8601 格式的持续时间。但是我到处搜索了一个 Oracle 过程或 SQL 的一部分,但没有成功。我看到有其他语言的示例,但没有 Oracle SQL 或 PL/SQL.

这是关于 the ISO standard 的一些信息。

我在 Oracle 11g 数据库上。

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 say;但是已经显示了小时你想显示分钟和秒,即使它们是零。如果没有,您可以调整(或删除)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 

您还可以使用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;