6

一部のデータを、ある Oracle スキーマ/テーブルから同じデータベースの新しいスキーマ/テーブルに移行しています。

移行スクリプトは次のことを行います。

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

元のデータを見ると、問題ないように見えます。ここに 1 つのレコードがあります。

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 の間の値である必要があります。

DATE 値の 7 バイトは、世紀 (+100)、年 (+100)、月、日、時 (+1)、分 (+1)、秒 (+1) の順に表されます。

このような無効な DATE 値を見たのは、Pro*C プログラムから DATE 値がバインド変数として提供されたときだけでした (バインド値は内部の 7 バイト表現で提供され、通常の検証ルーチンを完全にバイパスします)。 2 月 30 日のように無効な日付をキャッチします)

投稿した Oracle 構文を考えると、あなたが見ている動作を期待する理由はありません。

これは疑似異常 (メモリ破損?) であるか、またはこれが繰り返し発生する場合は、Oracle コードの欠陥 (バグ) です。それが Oracle コードの欠陥である場合、最も可能性の高い容疑者は、パッチが適用されていないリリースの「新しい」機能です。

(CAST は、他のデータベースで長年使用されている標準的な SQL 関数であることは知っています。私は古参で、Oracle の構文レパートリーに CAST を導入したことはありません。それがどのバージョンの Oracle であったかはわかりません。 CAST を導入しましたが、それが最初に登場したリリースでは、私はそれを避けていたでしょう。)


大きな「赤い旗」(別のコメント投稿者が指摘した)はCAST( datecol AS DATE).

オプティマイザーがそれを date_col と同等に扱うことを期待するでしょう...しかし、過去の経験TO_NUMBER( number_col )から、オプティマイザーによって実際に解釈されることが示されていTO_NUMBER( TO_CHAR ( number_col ) )ます。

その不要な CAST で同様のことが起こっているのではないかと思います。


あなたが示したその1つのレコードに基づいて、問題は分または秒の「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 に答える