考えられるすべての日付形式について適切な考えがある場合は、ブルートフォースを使用する方が簡単な場合があります:
create or replace function clean_date
( p_date_str in varchar2)
return date
is
l_dt_fmt_nt sys.dbms_debug_vc2coll := sys.dbms_debug_vc2coll
('DD-MON-YYYY', 'DD-MON-YY', 'DD-MM-YYYY', 'MM-DD-YYYY', 'YYYY-MM-DD'
, 'DD/MM/YYYY', 'MM/DD/YYYY', 'YYYY/MM/DD', 'DD/MM/YY', 'MM/DD/YY');
return_value date;
begin
for idx in l_dt_fmt_nt.first()..l_dt_fmt_nt.last()
loop
begin
return_value := to_date(p_date_str, l_dt_fmt_nt(idx));
exit;
exception
when others then null;
end;
end loop;
if return_value is null then
raise no_data_found;
end if;
return return_value;
exception
when no_data_found then
raise_application_error(-20000, p_date_str|| ' is unknown date format');
end clean_date;
/
最新バージョンのOracleは、日付変換に非常に寛容であることに注意してください。この関数は、リストにない形式で日付を処理し、いくつかの興味深い結果をもたらしました:
SQL> select clean_date('20160817') from dual;
CLEAN_DAT
---------
17-AUG-16
SQL> select clean_date('160817') from dual;
CLEAN_DAT
---------
16-AUG-17
SQL>
これは、緩いデータ整合性ルールに直面した場合の自動データクレンジングの限界を示しています。罪の賃金は堕落したデータです。
@AlexPooleは、'RR'
を使用する問題を提起します フォーマット。日付マスクのこの要素は、Y2K問題として導入されました。新しいミレニアムのほぼ20年後も、まだ話し合っているのはかなり気のめいることです。
とにかく、問題はこれです。この文字列をキャストすると'161225'
日付までにそれは何世紀になりますか?さて、'yymmdd'
2016-12-15
を提供します 。十分に公平ですが、'991225'
はどうでしょうか。 ?私たちが本当に望んでいる日付が2099-12-15
である可能性はどのくらいありますか ?ここが'RR'
フォーマットが登場します。基本的に、世紀のデフォルトです。00〜49の数字は20にデフォルト設定され、50〜99のデフォルトは19になります。このウィンドウは、2000年問題によって決定されました。2000年には、'98
の可能性が高くなりました。 近い将来よりも最近の過去を参照し、同様のロジックを'02
に適用しました 。したがって、1950年の中間点です。これは固定点であることに注意してください。 スライディングウィンドウではありません。 2000年からさらに進むと、ピボットポイントの有用性が低下します。詳細をご覧ください。
とにかく、重要な点は、「RRRR」が他の日付形式とうまく機能しないことです:to_date('501212', 'rrrrmmdd') hurls
ora-01843:有効な月. So, use
を使用します 'RR'and test for it before using
'YYYY'`。したがって、私の改訂された関数(多少の整理を含む)は次のようになります:
create or replace function clean_date
( p_date_str in varchar2)
return date
is
l_dt_fmt_nt sys.dbms_debug_vc2coll := sys.dbms_debug_vc2coll
('DD-MM-RR', 'MM-DD-RR', 'RR-MM-DD', 'RR-DD-MM'
, 'DD-MM-YYYY', 'MM-DD-YYYY', 'YYYY-MM-DD', 'YYYY-DD-MM');
return_value date;
begin
for idx in l_dt_fmt_nt.first()..l_dt_fmt_nt.last()
loop
begin
return_value := to_date(p_date_str, l_dt_fmt_nt(idx));
exit;
exception
when others then null;
end;
end loop;
if return_value is null then
raise no_data_found;
end if;
return return_value;
exception
when no_data_found then
raise_application_error(-20000, p_date_str|| ' is unknown date format');
end clean_date;
/
重要な点は残っています。日付の解釈に関しては、この機能をどれだけ賢くすることができるかには限界があるので、最適な方法でリードするようにしてください。ほとんどの日付文字列が日-月-年に適合すると思う場合は、それを最初に置きます。あなたはまだいくつかの間違ったキャストを得るでしょう、しかしあなたが年-月-日でリードするならばそれより少ないです。