web-dev-qa-db-ja.com

Oracle DATE列に格納されている無効な(破損した)値を識別する方法

Oracle 10.2.0.5

DATE列に「無効な」値があるテーブルの行を識別する最も簡単な方法は何ですか。ここで「無効」とは、日付値に関するOracleの規則に違反するバイナリ表現を意味します。

最近、列に無効な日付が格納されているという問題が発生しました。

クエリ述語を使用して、特定の問題のある行を見つけることができました。

  WHERE TO_CHAR(date_expr,'YYYYMMDDHH24MISS') = '00000000000000'

私が持っていた場合、世紀のバイトは無効でした...

 select dump(h.bid_close_date) from mytable h where h.id = 54321

 Typ=12 Len=7: 220,111,11,2,1,1,1

世紀のバイトは100 + 2桁の世紀である必要があります。この場合、世紀の値が「120」であるかのように、さらに100が追加され、年が「12011」になります。 (データベースに無効なDATE値を取得するために私が知っている唯一の方法は、ネイティブの7バイトのDATE表現を使用してOCIを使用することです。)

この場合、TO_CHAR関数は識別可能な文字列を返しました。これは、不安定なDATE値を識別するために使用できます。

私の質問:DATE列に「無効な」値を持つ行を識別するためのより一般的またはより簡単なアプローチ(できればSQL SELECTステートメントを使用)はありますか?.

13
spencer7593

これは無効な月を識別します

SELECT rowid,
       pk_column,
       DUMP(date_column, 1010) AS dump1
FROM   table
WHERE  TO_NUMBER(SUBSTR(DUMP(date_column, 1010), INSTR(DUMP( date_column, 1010),
                                              ',', 1, 2
                                                     ) + 1,
                                  INSTR(DUMP(date_column, 1010), ',', 1, 3) - (
                                  INSTR(DUMP( date_column, 1010), ',', 1, 2) + 1
                                  ))) = 0; 

同じwhere句を使用して更新すると、これらの場合、月番号がゼロであることがわかりました。

2
user8851928

これはかなり珍しいシナリオです(以前に似たようなものに出くわしたことがありますが)。より一般的な問題は、日付列に文字列として保持されている無効な日付を見つけることです。独自の日付バリデーターを構築することで、そのソリューションを状況に適合させることができます。

このようなもの:

create or replace function is_a_date 
    ( p_date in date )
    return varchar2
is
    d date;
begin
    d := to_date(to_char(p_date,  'SYYYYMMDDHH24MISS'),  'SYYYYMMDDHH24MISS') ;
    if d != p_date then
        return 'not a proper date';
    else
        return 'good date';
    end if;
exception
    when others  then
        return 'not a date';
end;
/ 

これにより、日付が文字列に変換され、元に戻ります。日付のキャストによってスローされた例外をキャッチします。最終製品が入力日と同じでない場合は、おそらく翻訳で何かが失われました。正直なところ、12011年の日付が文字列に正常にキャストされるかどうかはわかりません。したがって、これはベルトアンドブレースアプローチです。テストデータなしでこのユーティリティを書くのは少しトリッキーです!

このクエリは、すべての無効な日付を識別します。

 select h.id, dump(h.bid_close_date)
 from mytable h 
 where h.bid_close_date is not null
 and is_a_date(h.bid_close_date) != 'good date';
6
APC

関数を追加せずに、単純な述語

TO_CHAR(date_col,'YYYYMMDDHH24MISS') = '000000000000'

oracleDATE列に格納されている破損した値を特定するには十分なようです。関数の追加は不要のようです。破損した日付のチェックは、SQL SELECTステートメントで実行できる必要があり、ユーザーがデータベースに対するCREATEFUNCTION特権を持っている必要はありません。

3
spencer7593

おそらく日付が正当な値の範囲内にあることを確認することによって、チェック制約として実装できるメソッドを探します。

ただし、1066-10-14の日付を持つことが有効かどうかも自問してください。これは法的な価値ですが、たとえば、その日に請求書が印刷されていない可能性があります。したがって、無効な日付チェックを、アプリケーションのコンテキストで実際に有効と見なされるもののより大きな問題にロールインすることをお勧めします。

0
David Aldridge

SQL Error: ORA-01841: (full) year must be between -4713 and +9999, and not be 0 01841. 00000 - "(full) year must be between -4713 and +9999, and not be 0"がありました。

したがって、日付が悪い行を特定するために、次のことを行いました。

declare
    cursor mydates is select table_pk, your_date_col from table;
    c_date table.your_date_col%type;
    c_pk table.table_pk%type;
    testrow table.your_date_col%type;
begin
    open mydates;
    loop
    begin
        fetch mydates into c_pk, c_date;
        exit when mydates%notfound;
        testrow := TO_TIMESTAMP(c_date,'YYYY-MM-DD HH24:MI:SS');
    exception when others then
        dbms_output.put_line('bad file: ' || c_pk);
    end;
    end loop;
    close mydates;
end;

したがって、カーソルを作成し、要素をループして各要素をテストし、識別子を表示するだけで、問題のある行を簡単に見つけることができました。

0
Fering