简体   繁体   English

将时间戳舍入到最接近的秒

[英]Rounding of timestamp to nearest second

I've got a table containing timestamp values, and I want to round each of these values to the nearest second, but I can't get it work properly. 我有一个包含时间戳记值的表,我想将这些值中的每一个都舍入到最接近的秒数,但是我无法使其正常工作。

My test data and approaches so far: 到目前为止,我的测试数据和方法:

with v_data as
 (select to_timestamp('2012-12-10 10:49:30.00000000',
                      'YYYY-MM-DD HH24:mi:ss.FF8') base_val,
         to_timestamp('2012-12-10 10:49:30',
                      'YYYY-MM-DD HH24:mi:ss') expected
    from dual
  union all
  select to_timestamp('2012-12-10 10:49:30.46300000',
                      'YYYY-MM-DD HH24:mi:ss.FF8') base_val,
         to_timestamp('2012-12-10 10:49:30',
                      'YYYY-MM-DD HH24:mi:ss') expected
    from dual
  union all
  select to_timestamp('2012-12-10 10:49:30.50000000',
                      'YYYY-MM-DD HH24:mi:ss.FF8') base_val,
         to_timestamp('2012-12-10 10:49:31',
                      'YYYY-MM-DD HH24:mi:ss') expected
    from dual
  union all
  select to_timestamp('2012-12-10 10:49:30.56300000',
                      'YYYY-MM-DD HH24:mi:ss.FF8') base_val,
         to_timestamp('2012-12-10 10:49:31',
                      'YYYY-MM-DD HH24:mi:ss') expected

    from dual
  union all
  select to_timestamp('2012-12-10 10:49:30.99999999',
                      'YYYY-MM-DD HH24:mi:ss.FF8') base_val,
         to_timestamp('2012-12-10 10:49:31',
                      'YYYY-MM-DD HH24:mi:ss') expected
    from dual)
select v1.base_val,
       v1.expected,
       v1.base_val + (0.5 / 86400) solution_round,
       cast(v1.base_val as date) as solution_cast,
       extract(second from v1.base_val) - trunc(extract(second from v1.base_val)) fractional_seconds,
       v1.base_val -
       (extract(second from v1.base_val) - trunc(extract(second from v1.base_val))) / 86400 solution_add
  from v_data v1

All my solutions have a flaw: 我所有的解决方案都有一个缺陷:

  • solution_round always rounds up solution_round总是四舍五入
  • solution_cast works up to 11gR1, but in 11gR2, it always rounds down (cause: Oracle changed the behaviour - it now truncates instead of rounding, see https://forums.oracle.com/forums/thread.jspa?threadID=2242066 ) solution_cast最多可以运行11gR1,但是在11gR2中,它总是四舍五入(原因:Oracle改变了行为-现在它被截断而不是四舍五入,请参见https://forums.oracle.com/forums/thread.jspa?threadID=2242066
  • solution_add returns 10:49:29 instead of 10:49:31 for the last three rows solution_add返回最后三行的10:49:29而不是10:49:31

I guess solution_add should work, and I just made some stupid mistake :-) solution_add应该能用,我只是犯了一些愚蠢的错误:-)

EDIT: 编辑:

Ben's solution (see below) works for me, but relying on to_char(timestamp, 'FF') seems to be dangerous - the number of digits returned depends on the definition of the timestamp. Ben的解决方案(见下文)对我有用,但是依靠to_char(timestamp,'FF')似乎很危险-返回的位数取决于时间戳的定义。

I'm using to_char(timestamp, 'FF3') instead, which seems to return milliseconds reliably. 我使用的是to_char(timestamp,'FF3'),它似乎可靠地返回了毫秒。

My preferred method would be to use a CASE statement and the fact that you can convert the fractional seconds to a number, ie: 我的首选方法是使用CASE语句,并且可以将小数秒转换为数字,即:

select base_val, expected
     , to_timestamp(to_char(base_val,'YYYY-MM-DD HH24:mi:ss'),'YYYY-MM-DD HH24:mi:ss')
        + case when to_number(to_char(base_val, 'FF8')) >= 500000000 
                    then interval '1' second
               else interval '0' second
          end as solution_add
  from v_data

This removes the fractional seconds. 这消除了小数秒。 Then works out whether the fractional seconds portion of your TIMESTAMP is 0.5 seconds, or more. 然后计算出TIMESTAMP的小数秒部分是否为0.5秒或更多。 If so then add a second, otherwise don't. 如果是这样,则添加第二个,否则不要添加。

I find it a lot clearer and easier to understand what's going on. 我发现它更清楚,更容易理解发生了什么。 It returns the following: 它返回以下内容:

with v_data as
 (select to_timestamp('2012-12-10 10:49:30.00000000',
                      'YYYY-MM-DD HH24:mi:ss.FF8') base_val,
         to_timestamp('2012-12-10 10:49:30',
                      'YYYY-MM-DD HH24:mi:ss') expected
    from dual
  union all
  select to_timestamp('2012-12-10 10:49:30.46300000',
                      'YYYY-MM-DD HH24:mi:ss.FF8') base_val,
         to_timestamp('2012-12-10 10:49:30',
                      'YYYY-MM-DD HH24:mi:ss') expected
    from dual
  union all
  select to_timestamp('2012-12-10 10:49:30.50000000',
                      'YYYY-MM-DD HH24:mi:ss.FF8') base_val,
         to_timestamp('2012-12-10 10:49:31',
                      'YYYY-MM-DD HH24:mi:ss') expected
    from dual
  union all
  select to_timestamp('2012-12-10 10:49:30.56300000',
                      'YYYY-MM-DD HH24:mi:ss.FF8') base_val,
         to_timestamp('2012-12-10 10:49:31',
                      'YYYY-MM-DD HH24:mi:ss') expected
    from dual
  union all
  select to_timestamp('2012-12-10 10:49:30.99999999',
                      'YYYY-MM-DD HH24:mi:ss.FF8') base_val,
         to_timestamp('2012-12-10 10:49:31',
                      'YYYY-MM-DD HH24:mi:ss') expected
    from dual
         )
select base_val, expected
     , to_timestamp(to_char(base_val, 'YYYY-MM-DD HH24:mi:ss'), 'YYYY-MM-DD HH24:mi:ss')
        + case when to_number(to_char(base_val, 'FF8')) >= 500000000 
                    then interval '1' second
               else interval '0' second
          end as solution_add
  from v_data;

BASE_VAL                     EXPECTED                     SOLUTION_ADD
---------------------------- ---------------------------- ----------------------------
10-DEC-12 10.49.30.000000000 10-DEC-12 10.49.30.000000000 10-DEC-12 10.49.30.000000000
10-DEC-12 10.49.30.463000000 10-DEC-12 10.49.30.000000000 10-DEC-12 10.49.30.000000000
10-DEC-12 10.49.30.500000000 10-DEC-12 10.49.31.000000000 10-DEC-12 10.49.31.000000000
10-DEC-12 10.49.30.563000000 10-DEC-12 10.49.31.000000000 10-DEC-12 10.49.31.000000000
10-DEC-12 10.49.30.999999990 10-DEC-12 10.49.31.000000000 10-DEC-12 10.49.31.000000000

your final approach works (solution_add) its just that you used - instead of +. 您最终的方法(solution_add)可以正常使用-而不是+。 the 11g behavious is due to a bug fix (in 10g and below, plsql used to behave as "trunc" when cast was used, whereas SQL behaved as round. Oracle decided PLSQL was right, and changed 11g accordingly. 11g的行为是由于错误修复(在10g及更低版本中,使用cast时,plsql以前表现为“ trunc”,而SQL表现得很圆。Oracle认为PLSQL是正确的,并相应地更改了11g。

ie use: 即使用:

   v1.base_val +
   (extract(second from v1.base_val) - trunc(extract(second from v1.base_val))) / 86400 solution_add

though i'd probably specify it explicitly to take away the implicit conversion from timestamp to date (avoiding the dodgy cast()) 尽管我可能会明确指定它以消除从时间戳到日期的隐式转换(避免使用狡猾的cast())

to_date(to_char(v1.base_val, 'dd-mm-yyyy hh24:mi:ss'), 'dd-mm-yyyy hh24:mi:ss')+
       (( extract(second from v1.base_val) - trunc(extract(second from v1.base_val))) / 86400)

eg: 例如:

SQL> select * from v$version;

BANNER
----------------------------------------------------------------
Oracle Database 10g Enterprise Edition Release 10.2.0.4.0 - 64bi
PL/SQL Release 10.2.0.4.0 - Production
CORE    10.2.0.4.0      Production
TNS for Linux: Version 10.2.0.4.0 - Production
NLSRTL Version 10.2.0.4.0 - Production

SQL> with v_data as
  2   (select to_timestamp('2012-12-10 10:49:30.00000000',
  3                        'YYYY-MM-DD HH24:mi:ss.FF8') base_val,
  4           to_timestamp('2012-12-10 10:49:30',
  5                        'YYYY-MM-DD HH24:mi:ss') expected
  6      from dual
  7    union all
  8    select to_timestamp('2012-12-10 10:49:30.46300000',
  9                        'YYYY-MM-DD HH24:mi:ss.FF8') base_val,
 10           to_timestamp('2012-12-10 10:49:30',
 11                        'YYYY-MM-DD HH24:mi:ss') expected
 12      from dual
 13    union all
 14    select to_timestamp('2012-12-10 10:49:30.50000000',
 15                        'YYYY-MM-DD HH24:mi:ss.FF8') base_val,
 16           to_timestamp('2012-12-10 10:49:31',
 17                        'YYYY-MM-DD HH24:mi:ss') expected
 18      from dual
 19    union all
 20    select to_timestamp('2012-12-10 10:49:30.56300000',
 21                        'YYYY-MM-DD HH24:mi:ss.FF8') base_val,
 22           to_timestamp('2012-12-10 10:49:31',
 23                        'YYYY-MM-DD HH24:mi:ss') expected
 24      from dual
 25    union all
 26    select to_timestamp('2012-12-10 10:49:30.99999999',
 27                        'YYYY-MM-DD HH24:mi:ss.FF8') base_val,
 28           to_timestamp('2012-12-10 10:49:31',
 29                        'YYYY-MM-DD HH24:mi:ss') expected
 30      from dual)
 31  select v1.base_val,
 32         v1.expected,
 33         v1.base_val +
 34         (extract(second from v1.base_val) - trunc(extract(second from v1.base_val))) / 86400 solution_add,
 35         to_date(to_char(v1.base_val, 'dd-mm-yyyy hh24:mi:ss'), 'dd-mm-yyyy hh24:mi:ss')+
 36         (( extract(second from v1.base_val) - trunc(extract(second from v1.base_val))) / 86400) solution_add2
 37    from v_data v1;

BASE_VAL                  EXPECTED                  SOLUTION_ADD              SOLUTION_ADD2
------------------------- ------------------------- ------------------------- -------------------------
10-dec-12 10:49:30000     10-dec-12 10:49:30000     10-dec-2012 10:49:30      10-dec-2012 10:49:30
10-dec-12 10:49:30463     10-dec-12 10:49:30000     10-dec-2012 10:49:30      10-dec-2012 10:49:30
10-dec-12 10:49:30500     10-dec-12 10:49:31000     10-dec-2012 10:49:31      10-dec-2012 10:49:31
10-dec-12 10:49:30563     10-dec-12 10:49:31000     10-dec-2012 10:49:31      10-dec-2012 10:49:31
10-dec-12 10:49:30999     10-dec-12 10:49:31000     10-dec-2012 10:49:31      10-dec-2012 10:49:31




SQL> select * from v$version;

BANNER
--------------------------------------------------------------------------------
Oracle Database 11g Enterprise Edition Release 11.2.0.2.0 - 64bit Production
PL/SQL Release 11.2.0.2.0 - Production
CORE    11.2.0.2.0      Production
TNS for Linux: Version 11.2.0.2.0 - Production
NLSRTL Version 11.2.0.2.0 - Production

SQL> with v_data as
  2   (select to_timestamp('2012-12-10 10:49:30.00000000',
  3                        'YYYY-MM-DD HH24:mi:ss.FF8') base_val,
  4           to_timestamp('2012-12-10 10:49:30',
  5                        'YYYY-MM-DD HH24:mi:ss') expected
  6      from dual
  7    union all
  8    select to_timestamp('2012-12-10 10:49:30.46300000',
  9                        'YYYY-MM-DD HH24:mi:ss.FF8') base_val,
 10           to_timestamp('2012-12-10 10:49:30',
 11                        'YYYY-MM-DD HH24:mi:ss') expected
 12      from dual
 13    union all
 14    select to_timestamp('2012-12-10 10:49:30.50000000',
 15                        'YYYY-MM-DD HH24:mi:ss.FF8') base_val,
 16           to_timestamp('2012-12-10 10:49:31',
 17                        'YYYY-MM-DD HH24:mi:ss') expected
 18      from dual
 19    union all
 20    select to_timestamp('2012-12-10 10:49:30.56300000',
 21                        'YYYY-MM-DD HH24:mi:ss.FF8') base_val,
 22           to_timestamp('2012-12-10 10:49:31',
 23                        'YYYY-MM-DD HH24:mi:ss') expected
 24      from dual
 25    union all
 26    select to_timestamp('2012-12-10 10:49:30.99999999',
 27                        'YYYY-MM-DD HH24:mi:ss.FF8') base_val,
 28           to_timestamp('2012-12-10 10:49:31',
 29                        'YYYY-MM-DD HH24:mi:ss') expected
 30      from dual)
 31  select v1.base_val,
 32         v1.expected,
 33         v1.base_val +
 34         (extract(second from v1.base_val) - trunc(extract(second from v1.base_val))) / 86400 solution_add,
 35         to_date(to_char(v1.base_val, 'dd-mm-yyyy hh24:mi:ss'), 'dd-mm-yyyy hh24:mi:ss')+
 36         (( extract(second from v1.base_val) - trunc(extract(second from v1.base_val))) / 86400) solution_add2
 37    from v_data v1;

BASE_VAL                  EXPECTED                  SOLUTION_ADD              SOLUTION_ADD2
------------------------- ------------------------- ------------------------- -------------------------
10-dec-12 10:49:30000     10-dec-12 10:49:30000     10-dec-12 10:49:30        10-dec-12 10:49:30
10-dec-12 10:49:30463     10-dec-12 10:49:30000     10-dec-12 10:49:30        10-dec-12 10:49:30
10-dec-12 10:49:30500     10-dec-12 10:49:31000     10-dec-12 10:49:31        10-dec-12 10:49:31
10-dec-12 10:49:30563     10-dec-12 10:49:31000     10-dec-12 10:49:31        10-dec-12 10:49:31
10-dec-12 10:49:30999     10-dec-12 10:49:31000     10-dec-12 10:49:31        10-dec-12 10:49:31

I am very, very late to this discussion but just in case someone searches, there is a one-line answer to this question: 对于这个讨论,我非常非常晚,但是以防万一有人搜索,这个问题只有一个答案:

First CAST to a TIMESTAMP(0) - it has no fractional seconds so they will be rounded. 第一次CAST到TIMESTAMP(0)-它没有小数秒,因此将四舍五入。 Then CAST the result to a DATE 然后将结果投放到DATE

alter session set nls_date_format='yyyy-mm-dd hh24:mi:ss';
with data as (select sys_extract_utc(localtimestamp) ts from dual)
select ts, cast(cast(ts as timestamp(0)) as date) dte
from data;

Have you tried converting to_date and doing the rounding at that point? 您是否尝试过将to_date转换并进行四舍五入?

http://www.java2s.com/Tutorial/Oracle/0260__Date-Timestamp-Functions/RoundingtotheNearestMinute.htm http://www.java2s.com/Tutorial/Oracle/0260__Date-Timestamp-Functions/RoundingtotheNearestMinute.htm

You should then be able to convert to timestamp. 然后,您应该能够转换为时间戳。

Obviously change the 'MI' on line 3 to 'SS' 显然将第3行的“ MI”更改为“ SS”

---UPDATE--- --- UPDATE ---

Casting to a date will round to the nearest second as the DATE datatype has a precision of seconds, so cast to a date and then cast to a timestamp: 转换为日期将舍入到最接近的秒,因为DATE数据类型的精度为秒,因此转换为日期然后转换为时间戳记:

to_timestamp(CAST( '2012-12-10 10:49:30.99999999' AS DATE)) to_timestamp(CAST('2012-12-10 10:49:30.99999999'AS DATE))

This is T-SQL (not sure if it works on Oracle) 这是T-SQL(不确定是否可以在Oracle上运行)

DECLARE @tblTime TABLE (ID INT, myTime DATETIME2);
INSERT INTO @tblTime SELECT 1, GETDATE()

SELECT  myTime
    , CONVERT( DATETIME2, CONVERT( VARCHAR, DATEADD( SECOND, CASE WHEN DATEPART( MS, myTime) >= 500 THEN 1 ELSE -1 END, myTime ), 20), 20 ) AS timeRounded
FROM    @tblTime

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM