为什么Oracle会从小数秒中剥离前导0?

时间:2012-07-10 13:53:24

标签: sql oracle timestamp

我正在验证一些批输入(即纯文本);验证的第一个阶段是确保我必须排序的列实际上是正确的timestamp格式,然后再尝试将其放入我的时间戳。在这种情况下'yyyy/mm/dd hh24:mi:ss:ff2'

但是,似乎Oracle从时间戳格式的小数秒精度中删除了前导0。例如,假设009的精度为2(或更小),0099为精度,但不是0090。前两个例子显然是不正确的。看来,就日期时间格式模型而言,小数秒精度是精度,不包括前导0。

无论精确度如何,行为似乎都会发生。

这个例子是正确的:

select to_timestamp('2012/06/20 05:12:41:91','yyyy/mm/dd hh24:mi:ss:ff2') t
  from dual;

T
---------------------------------------------------------------------------
20-JUN-12 05.12.41.910000000

这些示例不正确:

我希望会出现错误但可以处理它被截断。

select to_timestamp('2012/06/20 05:12:41:091','yyyy/mm/dd hh24:mi:ss:ff2') t
  from dual;

 T
---------------------------------------------------------------------------
20-JUN-12 05.12.41.091000000

select to_timestamp('2012/06/20 05:12:41:0091','yyyy/mm/dd hh24:mi:ss:ff2') t
  from dual;

T
---------------------------------------------------------------------------
20-JUN-12 05.12.41.009100000

select to_timestamp('2012/06/20 05:12:41:00091','yyyy/mm/dd hh24:mi:ss:ff2') t
  from dual;

T
---------------------------------------------------------------------------
20-JUN-12 05.12.41.000910000

此错误是正确的;它的小数秒精度为3.

select to_timestamp('2012/06/20 05:12:41:901','yyyy/mm/dd hh24:mi:ss:ff2') t
  from dual;
select to_timestamp('2012/06/20 05:12:41:901','yyyy/mm/dd hh24:mi:ss:ff2') t
                    *
ERROR at line 1:
ORA-01880: the fractional seconds must be between 0 and 999999999

我正在使用版本11.2.0.1.0,但这种行为也出现在11.1.0.6.0和9.2.0.1.0中,所以它显然已经存在了一段时间。

这是我以前没有意识到的“功能”吗?

解决方案似乎假设所有时间戳都具有6位数的精度,但是还有另一个可以实际验证我给出的数据是否正确?

1 个答案:

答案 0 :(得分:1)

我确定你已经有了一个计划,但我认为我有一个游戏。 to_char似乎用.ff2截断为两位数 - 这个逻辑在我身上丢失了 - 所以如果你很乐意将所提供的值截断,你可以通过它反弹(在一个丑陋的,不应该是必要的方式):

select to_timestamp(
        to_char(
            to_timestamp('2012/06/20 05:12:41:091',
                'yyyy/mm/dd hh24:mi:ss:ff9'),
            'yyyy/mm/dd hh24:mi:ss:ff2'),
        'yyyy/mm/dd hh24:mi:ss:ff2') t
from dual;

T
---------------------------------------------------------------------------
20-JUN-12 05.12.41.090000000

或者你可以把它放在一个功能当然:

create or replace function my_to_timestamp(p_str varchar2)
return timestamp is
begin
    return to_timestamp(
            to_char(
                to_timestamp(p_str, 'yyyy/mm/dd hh24:mi:ss:ff9'),
                'yyyy/mm/dd hh24:mi:ss:ff2'),
            'yyyy/mm/dd hh24:mi:ss:ff2');
end;
/

select my_to_timestamp('2012/06/20 05:12:41:91') from dual;

MY_TO_TIMESTAMP('2012/06/2005:12:41:91')
---------------------------------------------------------------------------
20-JUN-12 05.12.41.910000000

select my_to_timestamp('2012/06/20 05:12:41:091') from dual;

MY_TO_TIMESTAMP('2012/06/2005:12:41:091')
---------------------------------------------------------------------------
20-JUN-12 05.12.41.090000000

select my_to_timestamp('2012/06/20 05:12:41:901') from dual;

MY_TO_TIMESTAMP('2012/06/2005:12:41:901')
---------------------------------------------------------------------------
20-JUN-12 05.12.41.900000000

或者您可以使用相同的机制使其出错:

create or replace function my_to_timestamp(p_str varchar2)
return timestamp is
    ts timestamp;
begin
    ts := to_timestamp(p_str, 'yyyy/mm/dd hh24:mi:ss:ff9');
    if ts != to_timestamp(
                to_char(
                    to_timestamp(p_str, 'yyyy/mm/dd hh24:mi:ss:ff9'),
                    'yyyy/mm/dd hh24:mi:ss:ff2'),
                'yyyy/mm/dd hh24:mi:ss:ff2')
    then
        raise program_error;
    end if;
    return ts;
end;
/

select my_to_timestamp('2012/06/20 05:12:41:91') from dual;

MY_TO_TIMESTAMP('2012/06/2005:12:41:91')
---------------------------------------------------------------------------
20-JUN-12 05.12.41.910000000

select my_to_timestamp('2012/06/20 05:12:41:091') from dual;

select my_to_timestamp('2012/06/20 05:12:41:091') from dual
       *
ERROR at line 1:
ORA-06501: PL/SQL: program error
ORA-06512: at "SCOTT.MY_TO_TIMESTAMP", line 12

select my_to_timestamp('2012/06/20 05:12:41:901') from dual;

select my_to_timestamp('2012/06/20 05:12:41:901') from dual
       *
ERROR at line 1:
ORA-06501: PL/SQL: program error
ORA-06512: at "SCOTT.MY_TO_TIMESTAMP", line 12

你可以添加一个异常和pragma来让它抛出ORA-01880,但我不确定该消息是否完全有用。