【问题标题】:Calculating difference between two timestamps in Oracle in milliseconds以毫秒为单位计算Oracle中两个时间戳之间的差异
【发布时间】:2012-07-22 00:13:34
【问题描述】:

如何计算 Oracle 中两个时间戳之间的时间差(以毫秒为单位)?

【问题讨论】:

  • @Matt 那个答案并没有显示出毫秒级别的差异
  • 马特,“可能重复”的问题没有回答我的问题。
  • 您真的是指“两个日期之间”吗? Oracle DATE 只有秒级的精度。 Oracle TIMESTAMP 具有亚秒级精度——通常是毫秒或微秒,具体取决于平台。
  • 谢谢。然后我需要两个时间戳之间的差异。将更新问题。

标签: sql oracle datetime


【解决方案1】:

我知道很多人认为这个解决方案简单明了:

create table diff_timestamp (
f1 timestamp
, f2 timestamp);

insert into diff_timestamp values(systimestamp-1, systimestamp+2);
commit;

select cast(f2 as date) - cast(f1 as date) from diff_timestamp;

宾果游戏!

【讨论】:

  • 虽然这有效,但您正在抛弃精度和准确性。精度原因 DATE 只下降到秒,而 TIMESTAMP 支持小数秒。 DATE 的准确性不支持时区,而 TIMESTAMP 支持。 2 个时间戳都为“07:25:17.123”,当它们来自不同时区时,它们的时间可能不同。
【解决方案2】:

I) 如果您需要计算两个时间戳列之间经过的时间(以秒为单位),请尝试以下操作:

SELECT 
    extract ( day from (end_timestamp - start_timestamp) )*86400 
    + extract ( hour from (end_timestamp - start_timestamp) )*3600 
    + extract ( minute from (end_timestamp - start_timestamp) )*60 
    + extract ( second from (end_timestamp - start_timestamp) ) 
FROM table_name

II) 如果您只想以字符格式显示时差,请尝试以下操作:

SELECT to_char (end_timestamp - start_timestamp) FROM table_name

【讨论】:

  • 您的格式有问题。请使用 4 个空格或反引号 ` 来格式化您的代码
  • 错了,这只给出了几秒钟。 1 天 20 秒 - 5 小时 10 秒会给你 10 秒。它不计算组成部分的秒数
  • EoinS ... 立即尝试 SQL,它将以秒为单位为您提供一年内经过的时间
【解决方案3】:

我已经发布了here 一些将时间间隔转换为纳秒和纳秒转换为时间间隔的方法。这些方法具有纳秒级精度。

您只需将其调整为毫秒而不是纳秒。

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

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
))) * 100 AS MILLIS FROM DUAL;

MILLIS
1598434427263.027

【讨论】:

    【解决方案4】:
    Select date1 - (date2 - 1) * 24 * 60 *60 * 1000 from Table;
    

    【讨论】:

    • 有点像“WorkAround”,但能胜任
    • 我猜你的意思是 SELECT ( (timestamp1-1) - (timestamp2-1) ) * 86400000 from Table; 我喜欢它,即使我会选择 "+ 0" ;-) 。但是我认为你失去了毫秒精度,因为“-1”似乎将 TIMESTAMP 转换为 DATE(没有毫秒)..
    【解决方案5】:

    我知道这已被详尽地回答,但我想与大家分享我的功能。它使您可以选择是否希望您的答案以天、小时、分钟、秒或毫秒为单位。您可以对其进行修改以满足您的需要。

    CREATE OR REPLACE FUNCTION Return_Elapsed_Time (start_ IN TIMESTAMP, end_ IN TIMESTAMP DEFAULT SYSTIMESTAMP, syntax_ IN NUMBER DEFAULT NULL) RETURN VARCHAR2 IS
        FUNCTION Core (start_ IN TIMESTAMP, end_ IN TIMESTAMP DEFAULT SYSTIMESTAMP, syntax_ IN NUMBER DEFAULT NULL) RETURN VARCHAR2 IS
            day_ VARCHAR2(7); /* This means this FUNCTION only supports up to 99 days */
            hour_ VARCHAR2(9); /* This means this FUNCTION only supports up to 999 hours, which is over 41 days */
            minute_ VARCHAR2(12); /* This means this FUNCTION only supports up to 9999 minutes, which is over 17 days */
            second_ VARCHAR2(18); /* This means this FUNCTION only supports up to 999999 seconds, which is over 11 days */
            msecond_ VARCHAR2(22); /* This means this FUNCTION only supports up to 999999999 milliseconds, which is over 11 days */
            d1_ NUMBER;
            h1_ NUMBER;
            m1_ NUMBER;
            s1_ NUMBER;
            ms_ NUMBER;
            /* If you choose 1, you only get seconds. If you choose 2, you get minutes and seconds etc. */
            precision_ NUMBER; /* 0 => milliseconds; 1 => seconds; 2 => minutes; 3 => hours; 4 => days */
            format_ VARCHAR2(2) := ', ';
            return_ VARCHAR2(50);
        BEGIN
            IF (syntax_ IS NULL) THEN
                precision_ := 0;
            ELSE
                IF (syntax_ = 0) THEN
                    precision_ := 0;
                ELSIF (syntax_ = 1) THEN
                    precision_ := 1;
                ELSIF (syntax_ = 2) THEN
                    precision_ := 2;
                ELSIF (syntax_ = 3) THEN
                    precision_ := 3;
                ELSIF (syntax_ = 4) THEN
                    precision_ := 4;
                ELSE 
                    precision_ := 0;
                END IF;
            END IF;
            SELECT EXTRACT(DAY FROM (end_ - start_)) INTO d1_ FROM DUAL;
            SELECT EXTRACT(HOUR FROM (end_ - start_)) INTO h1_ FROM DUAL;
            SELECT EXTRACT(MINUTE FROM (end_ - start_)) INTO m1_ FROM DUAL;
            SELECT EXTRACT(SECOND FROM (end_ - start_)) INTO s1_ FROM DUAL;
            IF (precision_ = 4) THEN
                IF (d1_ = 1) THEN
                    day_ := ' day';
                ELSE
                    day_ := ' days';
                END IF;
                IF (h1_ = 1) THEN
                    hour_ := ' hour';
                ELSE
                    hour_ := ' hours';
                END IF;
                IF (m1_ = 1) THEN
                    minute_ := ' minute';
                ELSE
                    minute_ := ' minutes';
                END IF;
                IF (s1_ = 1) THEN
                    second_ := ' second';
                ELSE
                    second_ := ' seconds';
                END IF;
                return_ := d1_ || day_ || format_ || h1_ || hour_ || format_ || m1_ || minute_ || format_ || s1_ || second_;
                RETURN return_;
            ELSIF (precision_ = 3) THEN
                h1_ := (d1_ * 24) + h1_;
                IF (h1_ = 1) THEN
                    hour_ := ' hour';
                ELSE
                    hour_ := ' hours';
                END IF;
                IF (m1_ = 1) THEN
                    minute_ := ' minute';
                ELSE
                    minute_ := ' minutes';
                END IF;
                IF (s1_ = 1) THEN
                    second_ := ' second';
                ELSE
                    second_ := ' seconds';
                END IF;
                return_ := h1_ || hour_ || format_ || m1_ || minute_ || format_ || s1_ || second_;
                RETURN return_;
            ELSIF (precision_ = 2) THEN
                m1_ := (((d1_ * 24) + h1_) * 60) + m1_;
                IF (m1_ = 1) THEN
                    minute_ := ' minute';
                ELSE
                    minute_ := ' minutes';
                END IF;
                IF (s1_ = 1) THEN
                    second_ := ' second';
                ELSE
                    second_ := ' seconds';
                END IF;
                return_ := m1_ || minute_ || format_ || s1_ || second_;
                RETURN return_;
            ELSIF (precision_ = 1) THEN
                s1_ := (((((d1_ * 24) + h1_) * 60) + m1_) * 60) + s1_;
                IF (s1_ = 1) THEN
                    second_ := ' second';
                ELSE
                    second_ := ' seconds';
                END IF;
                return_ := s1_ || second_;
                RETURN return_;
            ELSE
                ms_ := ((((((d1_ * 24) + h1_) * 60) + m1_) * 60) + s1_) * 1000;
                IF (ms_ = 1) THEN
                    msecond_ := ' millisecond';
                ELSE
                    msecond_ := ' milliseconds';
                END IF;
                return_ := ms_ || msecond_;
                RETURN return_;
            END IF;
        END Core;
    BEGIN
        RETURN(Core(start_, end_, syntax_));
    END Return_Elapsed_Time;
    

    例如,如果我现在(12.10.2018 11:17:00.00)使用 Return_Elapsed_Time(TO_TIMESTAMP('12.04.2017 12:00:00.00', 'DD.MM.YYYY HH24:MI:SS .FF'),SYSTIMESTAMP),它应该返回类似:

    47344620000 milliseconds
    

    【讨论】:

      【解决方案6】:

      最好使用这样的程序:

      CREATE OR REPLACE FUNCTION timestamp_diff
      (
      start_time_in TIMESTAMP
      , end_time_in TIMESTAMP
      )
      RETURN NUMBER
      AS
      l_days NUMBER;
      l_hours NUMBER;
      l_minutes NUMBER;
      l_seconds NUMBER;
      l_milliseconds NUMBER;
      BEGIN
      SELECT extract(DAY FROM end_time_in-start_time_in)
      , extract(HOUR FROM end_time_in-start_time_in)
      , extract(MINUTE FROM end_time_in-start_time_in)
      , extract(SECOND FROM end_time_in-start_time_in)
      INTO l_days, l_hours, l_minutes, l_seconds
      FROM dual;
      
      l_milliseconds := l_seconds*1000 + l_minutes*60*1000 + l_hours*60*60*1000 + l_days*24*60*60*1000;
      RETURN l_milliseconds;
      
      END;
      

      您可以通过调用来查看:

      SELECT timestamp_diff (TO_TIMESTAMP('12.04.2017 12:00:00.00', 'DD.MM.YYYY HH24:MI:SS.FF'), 
                            TO_TIMESTAMP('12.04.2017 12:00:01.111', 'DD.MM.YYYY HH24:MI:SS.FF')) 
                  as milliseconds
          FROM DUAL;
      

      【讨论】:

        【解决方案7】:

        这是一个存储过程:

        CREATE OR REPLACE function timestamp_diff(a timestamp, b timestamp) return number is 
        begin
          return extract (day    from (a-b))*24*60*60 +
                 extract (hour   from (a-b))*60*60+
                 extract (minute from (a-b))*60+
                 extract (second from (a-b));
        end;
        /
        

        如果您还想击败否定他工作的 Oracle 开发人员的废话,请投票!

        因为第一次比较时间戳每个人都需要一个小时左右...

        【讨论】:

        • 我怀疑是单个开发人员提出了这种复杂程度。我怀疑这是委员会设计的
        • 没错!这可能更容易!
        • 我认为 Oracle 的座右铭是“我们本可以让您轻松应对,但我们认为让您受苦更有趣。” +1
        • "我们很难写,你应该很难用吧"
        【解决方案8】:

        以上有语法错误,请在oracle上使用:

        SELECT ROUND (totalSeconds / (24 * 60 * 60), 1) TotalTimeSpendIn_DAYS,
          ROUND (totalSeconds      / (60 * 60), 0) TotalTimeSpendIn_HOURS,
          ROUND (totalSeconds      / 60) TotalTimeSpendIn_MINUTES,
          ROUND (totalSeconds) TotalTimeSpendIn_SECONDS
        FROM
          (SELECT ROUND ( EXTRACT (DAY FROM timeDiff) * 24 * 60 * 60 + EXTRACT (HOUR FROM timeDiff) * 60 * 60 + EXTRACT (MINUTE FROM timeDiff) * 60 + EXTRACT (SECOND FROM timeDiff)) totalSeconds
          FROM
            (SELECT TO_TIMESTAMP(TO_CHAR( date2 , 'yyyy-mm-dd HH24:mi:ss'), 'yyyy-mm-dd HH24:mi:ss') - TO_TIMESTAMP(TO_CHAR(date1, 'yyyy-mm-dd HH24:mi:ss'),'yyyy-mm-dd HH24:mi:ss') timeDiff
            FROM TABLENAME
            )
        );
        

        【讨论】:

          【解决方案9】:

          在格式之间正确转换的时间戳,否则字段可能会被误解。

          当从表 TableXYZ 中考虑两个不同的日期(Date2、Date1)时,这是一个正确的工作示例。

          SELECT ROUND (totalSeconds / (24 * 60 * 60), 1) TotalTimeSpendIn_DAYS,
                 ROUND (totalSeconds / (60 * 60), 0) TotalTimeSpendIn_HOURS,
                 ROUND (totalSeconds / 60) TotalTimeSpendIn_MINUTES,
                 ROUND (totalSeconds) TotalTimeSpendIn_SECONDS
            FROM (SELECT ROUND (
                              EXTRACT (DAY FROM timeDiff) * 24 * 60 * 60
                            + EXTRACT (HOUR FROM timeDiff) * 60 * 60
                            + EXTRACT (MINUTE FROM timeDiff) * 60
                            + EXTRACT (SECOND FROM timeDiff))
                            totalSeconds,
                    FROM (SELECT TO_TIMESTAMP (
                                      TO_CHAR (Date2,
                                               'yyyy-mm-dd HH24:mi:ss')
                                    - 'yyyy-mm-dd HH24:mi:ss'),
                                 TO_TIMESTAMP (
                                    TO_CHAR (Date1,
                                             'yyyy-mm-dd HH24:mi:ss'),
                                    'yyyy-mm-dd HH24:mi:ss')
                                    timeDiff
                            FROM TableXYZ))
          

          【讨论】:

            【解决方案10】:

            更简单的解决方案:

            SELECT numtodsinterval(date1-date2,'day') time_difference from dates;
            

            对于时间戳:

            SELECT (extract(DAY FROM time2-time1)*24*60*60)+ 
            (extract(HOUR FROM time2-time1)*60*60)+
            (extract(MINUTE FROM time2-time1)*60)+
            extract(SECOND FROM time2-time1)
            into diff FROM dual;
            
            RETURN diff;
            

            【讨论】:

            • 日期是表格吗?不能双标吗?我需要表中没有的 2 个日期之间的差异
            • 这两个日期是从哪里来的?
            • Patrick,我刚刚更新了问题 - 我需要两个时间戳之间的差异(以毫秒为单位,而不是天)。
            • time1 和 time2 - 这些是 TIMESTAMP 类型吗?
            【解决方案11】:

            当您减去两个TIMESTAMP 类型的变量时,您会得到一个INTERVAL DAY TO SECOND,其中包括取决于平台的毫秒数和/或微秒数。如果数据库在 Windows 上运行,systimestamp 通常会有毫秒。如果数据库在 Unix 上运行,systimestamp 通常会有微秒。

              1  select systimestamp - to_timestamp( '2012-07-23', 'yyyy-mm-dd' )
              2*   from dual
            SQL> /
            
            SYSTIMESTAMP-TO_TIMESTAMP('2012-07-23','YYYY-MM-DD')
            ---------------------------------------------------------------------------
            +000000000 14:51:04.339000000
            

            您可以使用EXTRACT 函数提取INTERVAL DAY TO SECOND 的各个元素

            SQL> ed
            Wrote file afiedt.buf
            
              1  select extract( day from diff ) days,
              2         extract( hour from diff ) hours,
              3         extract( minute from diff ) minutes,
              4         extract( second from diff ) seconds
              5    from (select systimestamp - to_timestamp( '2012-07-23', 'yyyy-mm-dd' ) diff
              6*           from dual)
            SQL> /
            
                  DAYS      HOURS    MINUTES    SECONDS
            ---------- ---------- ---------- ----------
                     0         14         55     37.936
            

            然后您可以将这些组件中的每一个转换为毫秒并将它们相加

            SQL> ed
            Wrote file afiedt.buf
            
              1  select extract( day from diff )*24*60*60*1000 +
              2         extract( hour from diff )*60*60*1000 +
              3         extract( minute from diff )*60*1000 +
              4         round(extract( second from diff )*1000) total_milliseconds
              5    from (select systimestamp - to_timestamp( '2012-07-23', 'yyyy-mm-dd' ) diff
              6*           from dual)
            SQL> /
            
            TOTAL_MILLISECONDS
            ------------------
                      53831842
            

            不过,通常情况下,使用 INTERVAL DAY TO SECOND 表示或将小时、分钟、秒等分开的列比计算两个 TIMESTAMP 值之间的总毫秒数更有用。

            【讨论】:

            • 抱歉,我本人不是 Oracle 专家。这将如何应对时区/夏令时?这里的所有答案似乎都使​​用24*60*60,这是假设所有日子都是24H,这是错误的。在 PostgreSQL 中,我会减去从 EXTRACT(EPOCH FROM ...) 返回的两个值,但 Oracle 似乎没有等价物。
            • @Bruno - 如果数据类型是TIMESTAMP WITH TIME ZONE,则减去两者所产生的间隔应该考虑到任何时区/夏令时转换。因此,从太平洋时间凌晨 2 点的 TIMESTAMP WITH TIME ZONE 中减去东部时间凌晨 5 点的 TIMESTAMP WITH TIME ZONE 将得到 0 秒的间隔。如果您只是使用没有时区的标准TIMESTAMP,则没有时区作为数据的一部分存储,因此无法进行此类更正。
            • plsql 我试过这个:declare ta timestamp; select systimestamp - to_timestamp( '2012-07-23', 'yyyy-mm-dd' ) into ta from dual; 它返回Error(5,21): PL/SQL: ORA-00932: inconsistent datatypes: expected NUMBER got INTERVAL DAY TO SECOND
            • @Kyrie - ta 需要是 interval day to second 数据类型,而不是 timestamp。从另一个中减去一个timestamp 返回一个interval 而不是另一个timestamp
            猜你喜欢
            • 2014-05-01
            • 2015-10-17
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多