sql >> Databasteknik >  >> RDS >> Oracle

oracle - konvertera många datumformat till ett enda formaterat datum

Om du har en bra uppfattning om alla möjliga datumformat kan det vara lättare att använda brute force:

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;
/

Var medveten om att moderna versioner av Oracle är ganska förlåtande med datumkonvertering. Den här funktionen hanterade datum i format som inte finns i listan, med några intressanta konsekvenser:

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> 

Vilket visar gränserna för automatiserad datarensning inför slappa regler för dataintegritet. Syndens lön är korrupta data.

@AlexPoole tar upp frågan om att använda 'RR' formatera. Detta element i datummasken introducerades som en Y2K kludge. Det är ganska deprimerande att vi fortfarande diskuterar det nästan två decennier in i det nya millenniet.

Hur som helst, problemet är detta. Om vi ​​castar den här strängen '161225' till ett datum vilket århundrade har den? Tja, 'yymmdd' ger 2016-12-15 . Helt rätt, men hur är det med '991225' ? Hur troligt är det att datumet vi verkligen vill ha är 2099-12-15 ? Det är här 'RR' formatet spelar in. I grund och botten är det standard för århundradet:siffrorna 00-49 som standard till 20, 50-99 som standard till 19. Detta fönster bestämdes av Y2K-problemet:år 2000 var det mer troligt att '98 hänvisade till det senaste förflutna än den närmaste framtiden, och liknande logik tillämpas på '02 . Därav halvvägs av 1950. Observera att detta är en fast punkt inte ett skjutfönster. När vi går längre från år 2000 desto mindre användbar blir den pivotpunkten. Ta reda på mer.

Hur som helst, nyckeln är att 'RRRR' inte spelar bra med andra datumformat:to_date('501212', 'rrrrmmdd') hurls ora-01843:inte en giltig månad. So, use 'RR'and test for it before using "ÅÅÅÅ"". Så min reviderade funktion (med lite städning) ser ut så här:

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;
/

Nyckelpunkten kvarstår:det finns en gräns för hur smart vi kan göra den här funktionen när det kommer till att tolka datum, så se till att du leder med den bästa passformen. Om du tror att de flesta av dina datumsträngar passar dag-månad-år sätt det först; du kommer fortfarande att få en del fel gips men mindre än om du leder med år-månad-dag.



  1. På uppgraderingsmetoden anropas inte i android sqlite

  2. Postgres VÄLJ där WHERE är UUID eller sträng

  3. XMLTABLE i PostgreSQL

  4. Ansluta en 64-bitars applikation till Acomba