更新期间Oracle日期损坏

时间:2011-08-11 17:49:59

标签: oracle data-migration corrupt-data

我正在将一些oracle架构/表中的数据迁移到同一数据库中的新架构/表。

迁移脚本执行以下操作:

create table newtable as select
  ...
  cast(ACTIVITYDATE as date) as ACTIVITY_DATE,
  ...
FROM oldtable where ACTIVITYDATE > sysdate - 1000;

如果我查看原始数据,它看起来很好 - 这是一条记录:

select 
  activitydate,
  to_char(activitydate, 'MON DD,YYYY'),
  to_char(activitydate, 'DD-MON-YYYY HH24:MI:SS'),
  dump(activitydate),
  length(activitydate)
from orginaltable  where oldpk =  1067514

结果:

18-NOV-10                 NOV 18,2010                        18-NOV-2010 12:59:15                          Typ=12 Len=7: 120,110,11,18,13,60,16  

迁移的数据,显示数据已损坏:

select 
  activity_date,
  to_char(activity_date, 'MON DD,YYYY'),
  to_char(activity_date, 'DD-MON-YYYY HH24:MI:SS'),
  dump(activity_date),
  length(activity_date)
from newtable
where id =  1067514

结果:

18-NOV-10                 000 00,0000                         00-000-0000 00:00:00                           Typ=12 Len=7: 120,110,11,18,13,0,16   

350k记录中约有5000个记录显示此问题。

任何人都可以解释这是怎么发生的吗?

2 个答案:

答案 0 :(得分:4)

更新:

我在Oracle支持网站上找不到任何已发布的对此特定类型的DATE损坏的引用。 (它可能在那里,我的快速搜索只是没有打开它。)

  • 检查数据库损坏日期的Baddate脚本[ID 95402.1]
  • 错误2790435 - 具有并行SELECT和类型转换的串行INSERT可以插入损坏的数据[ID 2790435.8]

DUMP()函数的输出显示日期值确实无效:

Typ=12 Len=7: 120,110,11,18,13,0,16 

我们希望分钟字节应该是一到六十之间的值,而不是零。

DATE值的7个字节依次表示世纪(+100),年(+100),月,日,小时(+1),分钟(+1),秒(+1)。< / p>

我唯一一次看到无效的DATE值,当DATE值作为绑定变量提供时,来自Pro * C程序(其中绑定值以内部7字节表示形式提供,完全绕过正常值)捕获无效日期的验证例程,例如2月30日)

根据您发布的Oracle语法,没有理由期待您所看到的行为。

这可能是一个虚假的异常(内存损坏?),或者如果这是可重复的,那么它就是Oracle代码中的一个缺陷(bug)。如果这是Oracle代码中的一个缺陷,最可能的嫌疑人将是未修补版本中的“新”功能。

(我知道CAST是一个标准的SQL函数,在其他数据库中存在很长时间。我想我已经老了,并且从未将它引入我的Oracle语法库中。我不知道Oracle的版本是什么它引入了CAST,但我会在它出现的第一个版本中远离它。)


另一位评论者指出的大“红旗”是CAST( datecol AS DATE)

您希望优化器将其视为等同于date_col ...但过去的经验告诉我们TO_NUMBER( number_col )实际上被优化程序解释为TO_NUMBER( TO_CHAR ( number_col ) )

我怀疑那些不需要的CAST会发生类似的情况。


根据您显示的那条记录,我怀疑问题是值为“59”值的分钟或秒,而可能是“23”的小时值,将显示错误。

我会尝试检查分钟,小时或秒存储为0的地方:

SELECT id, DUMP(activitydate)
  FROM newtable
 WHERE DUMP(activitydate) LIKE '%,0,%' 
    OR DUMP(activitydate) LIKE '%,0'

答案 1 :(得分:2)

我见过类似于spence7593的东西,再次使用Pro * C. 可以使用DBMS_STATS包以编程方式创建无效日期。 不确定是否有类似的机制来扭转这种情况。

create or replace function stats_raw_to_date (p_in raw) return date is
  v_date date;
  v_char varchar2(25);
begin
  dbms_stats.CONVERT_RAW_VALUE(p_in, v_date);
  return v_date;
exception
  when others then return null;
end;
/

select stats_raw_to_date(utl_raw.cast_to_raw(
          chr(120)||chr(110)||chr(11)||chr(18)||chr(13)||chr(0)||chr(16)))
from dual;