sql >> Base de Datos >  >> RDS >> Oracle

Oracle:convierte muchos formatos de fecha en una sola fecha formateada

Si tiene una buena idea de todos los formatos de fecha posibles, podría ser más fácil usar la fuerza bruta:

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

Tenga en cuenta que las versiones modernas de Oracle son bastante indulgentes con la conversión de fechas. Esta función manejó fechas en formatos que no están en la lista, con algunas consecuencias interesantes:

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> 

Lo que demuestra los límites de la limpieza de datos automatizada frente a las reglas de integridad de datos laxas. La paga del pecado son datos corruptos.

@AlexPoole plantea la cuestión de usar el 'RR' formato. Este elemento de la máscara de fecha se introdujo como una chapuza Y2K. Es bastante deprimente que todavía estemos discutiéndolo casi dos décadas después del nuevo milenio.

De todos modos, el tema es este. Si lanzamos esta cadena '161225' a una fecha que siglo tiene? Bueno, 'yymmdd' dará 2016-12-15 . Está bien, pero ¿qué pasa con '991225' ? ¿Qué tan probable es que la fecha que realmente queremos sea 2099-12-15? ? Aquí es donde el 'RR' El formato entra en juego. Básicamente, el valor predeterminado es el siglo:los números 00-49 predeterminados son 20, 50-99 predeterminados son 19. Esta ventana fue determinada por el problema Y2K:en 2000 era más probable que '98 se refería al pasado reciente que al futuro cercano, y se aplicaba una lógica similar a '02 . Por lo tanto, el punto medio de 1950. Tenga en cuenta que este es un punto fijo no una ventana corredera. A medida que nos alejamos del año 2000, menos útil se vuelve ese punto de pivote. Obtenga más información.

De todos modos, el punto clave es que 'RRRR' no funciona bien con otros formatos de fecha:to_date('501212', 'rrrrmmdd') hurls ora-01843:no es un . So, use 'RR'and test for it before using 'YYYY'`. Así que mi función revisada (con algunos arreglos) se ve así:

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

El punto clave sigue siendo:hay un límite en lo inteligente que podemos hacer esta función cuando se trata de interpretar fechas, así que asegúrese de comenzar con la mejor opción. Si cree que la mayoría de sus cadenas de fecha se ajustan a día-mes-año, póngala primero; aún obtendrá algunos lanzamientos incorrectos, pero menos si comienza con año-mes-día.