我有一个包含时间戳值的表,我想将这些值中的每一个舍入到最接近的秒,但我无法正常工作。
到目前为止我的测试数据和方法:
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
我所有的解决方案都存在缺陷:
我猜测 solution_add应该可行,我只是犯了一些愚蠢的错误: - )
修改
Ben的解决方案(见下文)适用于我,但依赖于to_char(时间戳,'FF')似乎很危险 - 返回的位数取决于时间戳的定义。
我正在使用to_char(时间戳,'FF3'),这似乎可靠地返回毫秒。
答案 0 :(得分:3)
我首选的方法是使用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
这将删除小数秒。然后计算出TIMESTAMP的小数秒部分是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
答案 1 :(得分:2)
你的最终方法(solution_add)只是你用过的 - 而不是+。 11g的行为是由于一个错误修复(在10g及以下,plsql曾经在使用强制转换时表现为“trunc”),而SQL表现为圆形.Oracle决定PLSQL是正确的,并相应地改变了11g。
即。使用方法:
v1.base_val +
(extract(second from v1.base_val) - trunc(extract(second from v1.base_val))) / 86400 solution_add
虽然我可能会明确指定它来取消从时间戳到日期的隐式转换(避免狡猾的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)
例如:
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
答案 2 :(得分:1)
对于这次讨论,我非常非常晚,但是以防万一有人搜索,对于这个问题只有一个答案:
第一次CAST到TIMESTAMP(0)-它没有小数秒,因此将四舍五入。 然后将结果投放到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;
答案 3 :(得分:0)
您是否尝试过转换to_date并在此时进行舍入?
http://www.java2s.com/Tutorial/Oracle/0260__Date-Timestamp-Functions/RoundingtotheNearestMinute.htm
然后您应该能够转换为时间戳。
显然将第3行的'MI'改为'SS'
--- --- UPDATE
转换为日期将舍入到最接近的秒,因为DATE数据类型的精度为秒,因此强制转换为日期,然后转换为时间戳:
to_timestamp(CAST('2012-12-10 10:49:30.99999999'作为日期))
答案 4 :(得分:-1)
这是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