6

我正在将一些数据从一个 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 条显示了这个问题。

谁能解释这是怎么发生的?

4

2 回答 2

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 

我们期望分钟字节应该是 1 到 60 之间的值,而不是 0。

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

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

鉴于您发布的 Oracle 语法,没有理由期待您看到的行为。

这要么是虚假异常(内存损坏?),要么是可重复的,那么这是 Oracle 代码中的缺陷(错误)。如果它是 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'
于 2011-08-11T20:00:29.497 回答
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;
于 2011-08-12T05:10:34.407 回答