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

Sql 将时间戳舍入到最接近的秒,sql,oracle,oracle11g,Sql,Oracle,Oracle11g,我有一个包含时间戳值的表,我想将每个值四舍五入到最接近的秒,但我无法让它正常工作 到目前为止,我的测试数据和方法: 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',

我有一个包含时间戳值的表,我想将每个值四舍五入到最接近的秒,但我无法让它正常工作

到目前为止,我的测试数据和方法:

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
我的所有解决方案都有一个缺陷:

  • 解决方案总是四舍五入
  • 解决方案_cast适用于11gR1,但在11gR2中,它总是向下舍入(原因:Oracle改变了行为-它现在截断而不是舍入,请参阅)
  • 解决方案_add返回10:49:29,而不是最后三行的10:49:31
我想解决办法应该行得通,我只是犯了个愚蠢的错误:-)

编辑:

Ben的解决方案(见下文)适用于我,但依赖to_char(timestamp,'FF')似乎很危险——返回的位数取决于时间戳的定义


我使用to_char(timestamp,'FF3'),它似乎可靠地返回毫秒

您是否尝试过转换为_日期并在该点进行舍入

然后,您应该能够转换为时间戳

显然,将第3行的“MI”改为“SS”

---更新---

转换到日期将舍入到最接近的秒,因为日期数据类型的精度为秒,所以转换到日期,然后转换到时间戳:

to_时间戳(强制转换('2012-12-10 10:49:30.9999999'为日期))

这是T-SQL(不确定它是否适用于Oracle)

您的最终方法有效(解决方案)您只使用了-而不是+。11g的行为是由于一个bug修复(在10g及以下版本中,plsql在使用cast时的行为是“trunc”,而SQL的行为是圆形的。Oracle认为plsql是正确的,并相应地更改了11g

i、 e.使用:

   v1.base_val +
   (extract(second from v1.base_val) - trunc(extract(second from v1.base_val))) / 86400 solution_add
虽然我可能会显式地指定它,以消除从时间戳到日期的隐式转换(避免不可靠的cast()

例如:


我的首选方法是使用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
这将删除小数秒。然后计算时间戳的小数秒部分是0.5秒还是更多。如果是,则添加一秒,否则不添加

我发现它更清晰、更容易理解正在发生的事情。它返回以下内容:

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

我非常非常晚才开始讨论,但万一有人搜索,这个问题只有一行答案:

第一次强制转换为时间戳(0)-它没有小数秒,因此它们将被舍入。 然后将结果转换为日期

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;

这里不应该有一些括号吗?
x+0.5/86400==x+(0.5/86400)
严格地说,括号是不必要的,因为/的优先级高于+。但是我添加了它们以使这更清楚,谢谢……是的,这就是我的意思。0.5/86400没有任何意义(
x+0.5/86400~=x+0.0000058
)。它不应该是
(x+0.5)/86400
?不,它不应该是。我想加半秒钟(0.5/(24*60*60),截断它并使用它(类似于“穷人的四舍五入”)好的,我以为我们讨论的是Unix时间戳。Oracle的版本听起来很糟糕。显然,舍入时没有SS选项,因为CAST(时间戳为日期)将自动舍入到最接近的秒。这在11gR2上不起作用,因为CAST()现在截断而不是舍入(请参阅我问题中的解决方案\u CAST)有趣的是,这似乎是一个已修复的错误。CAST将从11gR2开始截断,您需要使用to_DATE:让我知道这是否有效,我将更新答案。对不起,但我不清楚您到底在建议什么-我使用to_DATE(时间戳)?让我们知道这根本不起作用(即使在删除表变量时)抱歉,但这确实适用于T-SQL,我尝试过。但无论如何,您可以从中了解如何使其工作。基本上,它是一个用例语句,用于确定是否添加第二个。并使用舍入机制(转换/转换的格式将mmmm部分从原始日期中删除,并根据案例中的调整调整第二个基准。仅供参考:我已经撤销了我的接受,并接受了Ben的解决方案,因为IMO的解决方案更清晰、更容易理解,但我已经对你的方案进行了投票-感谢你的帮助。值得一提的是,精度n由to_char返回(时间戳,'FF')取决于时间戳的定义-例如,对于时间戳(3),它将返回千分之一秒,比较将无法按预期进行。但对于我的数据,这是可以的。它应该是FF8@FrankSchmitt。正如您所注意到的,它对您的情况没有影响,但最好指定。您可能会迟到,但该解决方案非常出色。谢谢。
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
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
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;