【问题标题】:Extracting the total number of seconds from an interval data-type从间隔数据类型中提取总秒数
【发布时间】:2012-04-22 22:29:31
【问题描述】:

当减去timestamps 时,返回值为interval 数据类型。有没有一种优雅的方法可以将此值转换为间隔中的(毫秒/微)秒总数,即整数。

以下方法可行,但不是很漂亮:

select abs( extract( second from interval_difference ) 
          + extract( minute from interval_difference ) * 60 
          + extract( hour from interval_difference ) * 60 * 60 
          + extract( day from interval_difference ) * 60 * 60 * 24
            )
  from ( select systimestamp - (systimestamp - 1) as interval_difference
           from dual )

在 SQL 或 PL/SQL 中有更优雅的方法吗?

【问题讨论】:

  • :我在某个地方找到了这个` select (TRUNC(SYSDATE) + out.interv - TRUNC(SYSDATE)) * 86400 from (select systimestamp -(systimestamp -1) as interv from dual )out`
  • 由于将返回的以秒为单位的间隔添加到一个固定精度的数字变量中,所以在cmets中提到的查询中会丢失秒的小数部分
  • 虽然它可能看起来不太优雅,但我仍然更喜欢这种基本解决方案,因为它不会那么容易受到“ORA-01873:间隔的前导精度太小”的影响。

标签: sql oracle plsql


【解决方案1】:

一个简单的方法:

select extract(day from (ts1-ts2)*86400) from dual;

这个想法是将间隔值转换为天数,乘以 86400 (= 24*60*60)。 然后提取“day”值,这实际上是我们想要的第二个值。

【讨论】:

  • 这是一个可爱的答案。
  • 我对此进行了调整以使用extract(day from (ts1-ts2)*86400*1000) / 1000 来获得毫秒精度。
  • 这仅适用于小间隔 - 低于 1000 秒。在那之后,你会遇到ORA-01873: the leading precision of the interval is too small
  • @ŠtefanOravec 我做了一个快速测试。我能得到的最大间隔是 Oracle 11.2.0.4 中的 11574 天,如下所示: > select extract(day from (systimestamp - (systimestamp - 11574))*86400) seconds from dual;秒 ---------- 999993600
  • @ZhaopingLua 在您的测试用例中显然有一些内部数据类型转换魔法,因为它因间隔数据类型而失败 - select extract(day from (numtodsinterval(10, 'hour')) * 86400) from dual; vs select extract(day from (numtodsinterval(1000, 'second')) * 86400) from dual; (Oracle 18cXE)
【解决方案2】:

希望对您有所帮助:

zep@dev> select interval_difference
      2        ,sysdate + (interval_difference * 86400) - sysdate as fract_sec_difference
      3  from   (select systimestamp - (systimestamp - 1) as interval_difference
      4          from   dual)
      5 ;

INTERVAL_DIFFERENCE                                                             FRACT_SEC_DIFFERENCE
------------------------------------------------------------------------------- --------------------
+000000001 00:00:00.375000                                                                 86400,375

通过你的测试:

zep@dev> select interval_difference
      2        ,abs(extract(second from interval_difference) +
      3        extract(minute from interval_difference) * 60 +
      4        extract(hour from interval_difference) * 60 * 60 +
      5        extract(day from interval_difference) * 60 * 60 * 24) as your_sec_difference
      6        ,sysdate + (interval_difference * 86400) - sysdate as fract_sec_difference
      7        ,round(sysdate + (interval_difference * 86400) - sysdate) as sec_difference
      8        ,round((sysdate + (interval_difference * 86400) - sysdate) * 1000) as millisec_difference
      9  from   (select systimestamp - (systimestamp - 1) as interval_difference
     10          from   dual)
     11  /

INTERVAL_DIFFERENCE                                                             YOUR_SEC_DIFFERENCE FRACT_SEC_DIFFERENCE SEC_DIFFERENCE MILLISEC_DIFFERENCE
------------------------------------------------------------------------------- ------------------- -------------------- -------------- -------------------
+000000001 00:00:00.515000                                                                86400,515            86400,515          86401            86400515

zep@dev> 

【讨论】:

  • 我最终解释了为什么在这里有效:stackoverflow.com/a/17413839/458741
  • 这不适用于一些 interval_difference 值。例如:select interval_difference, sysdate + (interval_difference * 86400) - sysdate as fract_sec_difference from (select systimestamp - (systimestamp - 1/3) as interval_difference from dual );
【解决方案3】:

我发现这行得通。显然,如果您使用时间戳进行算术运算,它们将转换为某种内部数据类型,当彼此相减时,将间隔作为数字返回。

容易吗?是的。优雅的?不。完成工作了吗?哦对了。

SELECT ( (A + 0) - (B + 0) ) * 24 * 60 * 60 
FROM
(
   SELECT SYSTIMESTAMP A,
          SYSTIMESTAMP - INTERVAL '1' MINUTE B
   FROM DUAL
);

【讨论】:

  • 这只给了我整整几秒钟。
  • 哎呀,真的。这个问题特别要求微/毫秒。不过,这是一种简单的方法来给出整秒钟的时间。
  • CAST(A AS DATE) 等价于 (A+0)
  • 带时间戳的算术与转换存储在表中的区间类型值不同
【解决方案4】:

不幸的是,我认为没有替代(或更优雅)的方法可以从 pl/sql 中的间隔类型计算总秒数。正如this article 提到的那样:

... unlike .NET, Oracle provides no simple equivalent to TimeSpan.TotalSeconds.

因此,从间隔中提取日、小时等并将它们与相应的值相乘似乎是唯一的方法。

【讨论】:

    【解决方案5】:

    基于zep's answer,为了您的方便,我将其封装成一个函数:

    CREATE OR REPLACE FUNCTION intervalToSeconds( 
         pMinuend TIMESTAMP , pSubtrahend TIMESTAMP ) RETURN NUMBER IS
    
    vDifference INTERVAL DAY TO SECOND ; 
    
    vSeconds NUMBER ;
    
    BEGIN 
    
    vDifference := pMinuend - pSubtrahend ;
    
    SELECT EXTRACT( DAY    FROM vDifference ) * 86400
         + EXTRACT( HOUR   FROM vDifference ) *  3600
         + EXTRACT( MINUTE FROM vDifference ) *    60
         + EXTRACT( SECOND FROM vDifference )
      INTO
        vSeconds 
      FROM DUAL ;
    
      RETURN vSeconds ;
    
    END intervalToSeconds ; 
    

    【讨论】:

    • 谢谢,但是函数的效率远不如SQL。仅在 SQL/PL/SQL 上下文切换中,每条记录都会损失大约一毫秒。也不需要从对偶中选择或将结果分配给变量,您可以直接返回它。我会像这样编写相同的函数:sqlfiddle.com/#!4/42d23/1。但是,我想强调的是,尽可能不要使用函数。
    • 另外,现在我开始考虑代码是我的......我在问是否有更好的方法!欢迎来到 Stack Overflow :-)。
    【解决方案6】:

    使用以下查询:

    select (cast(timestamp1 as date)-cast(timestamp2 as date))*24*60*60)
    

    【讨论】:

      【解决方案7】:

      类似于@Zhaoping Lu 的回答,但直接提取秒数而不是从天数中获取秒数。

      SELECT extract(second from (end_date - start_date)) as "Seconds number"
      FROM my_table
      

      (在 PostgresSQL 9.6.1 上工作)

      【讨论】:

        【解决方案8】:

        一种将时间戳转换为纳秒的更短方法。

        SELECT (EXTRACT(DAY FROM (
            SYSTIMESTAMP --Replace line with desired timestamp --Maximum value: TIMESTAMP '3871-04-29 10:39:59.999999999 UTC'
        - TIMESTAMP '1970-01-01 00:00:00 UTC') * 24 * 60) * 60 + EXTRACT(SECOND FROM
            SYSTIMESTAMP --Replace line with desired timestamp
        )) *  1000000000 AS NANOS FROM DUAL;
        
        NANOS
        1598434427263027000
        

        一种将纳秒转换为时间戳的方法。

        SELECT TIMESTAMP '1970-01-01 00:00:00 UTC' + numtodsinterval(
            1598434427263027000 --Replace line with desired nanoseconds
        / 1000000000, 'SECOND') AS TIMESTAMP FROM dual;
        
        TIMESTAMP
        26/08/20 09:33:47,263027000 UTC
        

        正如所料,上述方法的结果不受时区影响。

        一种将时间间隔转换为纳秒的更短方法。

        SELECT (EXTRACT(DAY FROM (
            INTERVAL '+18500 09:33:47.263027' DAY(5) TO SECOND --Replace line with desired interval --Maximum value: INTERVAL '+694444 10:39:59.999999999' DAY(6) TO SECOND(9) or up to 3871 year
        ) * 24 * 60) * 60 + EXTRACT(SECOND FROM (
            INTERVAL '+18500 09:33:47.263027' DAY(5) TO SECOND --Replace line with desired interval
        ))) * 1000000000 AS NANOS FROM DUAL;
        
        NANOS
        1598434427263027000
        

        一种将纳秒转换为间隔的方法。

        SELECT numtodsinterval(
            1598434427263027000 --Replace line with desired nanoseconds
        / 1000000000, 'SECOND') AS INTERVAL FROM dual;
        
        INTERVAL
        +18500 09:33:47.263027
        

        将 1000000000 替换为 1000,例如,如果您希望使用毫秒而不是纳秒。

        我尝试了一些发布的方法,但是当将间隔乘以 86400 时出现异常“ORA-01873:间隔的前导精度太小”,所以我决定发布有效的方法对我来说。

        【讨论】:

          【解决方案9】:

          SELECT to_char(ENDTIME,'yyyymmddhh24missff')-to_char(STARTTIME,'yyyymmddhh24missff') AS DUR 从双; yyyymmddhh24miss- 将在 SEC 中给出持续时间 yyyymmddhh24mi DURATION IN MIN yyyymmddhh24 - 持续时间 - 小时 yyyymmdd DURATION IN DAYS

          【讨论】:

          • 请格式化代码并解释您发布的答案。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-12-12
          • 2013-02-22
          • 1970-01-01
          • 1970-01-01
          • 2019-05-18
          相关资源
          最近更新 更多