oracle-将许多日期格式转换为单个格式的日期

Ady*_*can 3 oracle format date

我想将包含日期的字符串带入单一格式的日期。例如:

  • 2012年13月6日至2012年6月13日
  • 2012年13月6日至2012年6月13日
  • 2012年6月13日至2012年6月13日
  • 2012年6月13日至2012年6月13日
  • ...

我试图删除所有特殊字符,然后使用函数将该字符串转换为单一日期格式。我的函数返回了更多异常,我不知道为什么...

功能:

CREATE OR REPLACE FUNCTION normalize_date (data_in IN VARCHAR2)
    RETURN DATE
IS
    tmp_month         VARCHAR2 (3);
    tmp_day           VARCHAR2 (2);
    tmp_year          VARCHAR2 (4);
    TMP_YEAR_NUMBER   NUMBER;
    result            DATE;
BEGIN
    tmp_day := SUBSTR (data_in, 1, 2);
    tmp_year := SUBSTR (data_in, -4);

    --if(REGEXP_LIKE(SUBSTR(data_in,3,2), '[:alpha:]')) then 
    if(SUBSTR(data_in,3,1) in ('a','j','i','f','m','s','o','n','d','A','J','I','F','M','S','O','N','D')) then      
    tmp_month := UPPER(SUBSTR (data_in, 3, 3));
    else
    tmp_month := SUBSTR (data_in, 3, 2);
    end if;

    DBMS_OUTPUT.put_line (tmp_year);

    TMP_YEAR_NUMBER := TO_NUMBER (tmp_year);

    IF (tmp_month = 'JAN')
    THEN
        tmp_month := '01';
    END IF;

    IF (tmp_month = 'FEB')
    THEN
        tmp_month := '02';
    END IF;

    IF (tmp_month = 'MAR')
    THEN
        tmp_month := '03';
    END IF;

    IF (tmp_month = 'APR')
    THEN
        tmp_month := '04';
    END IF;

    IF (tmp_month = 'MAY')
    THEN
        tmp_month := '05';
    END IF;

    IF (tmp_month = 'JUN')
    THEN
        tmp_month := '06';
    END IF;

    IF (tmp_month = 'JUL')
    THEN
        tmp_month := '07';
    END IF;

    IF (tmp_month = 'AUG')
    THEN
        tmp_month := '08';
    END IF;

    IF (tmp_month = 'SEP')
    THEN
        tmp_month := '09';
    END IF;

    IF (tmp_month = 'OCT')
    THEN
        tmp_month := '10';
    END IF;

    IF (tmp_month = 'NOV')
    THEN
        tmp_month := '11';
    END IF;

    IF (tmp_month = 'DEC')
    THEN
        tmp_month := '12';
        END IF;

   -- dbms_output.put_line(tmp_day || '~'||tmp_year || '~' ||tmp_month);

    IF (LENGTH (tmp_day || tmp_year || tmp_month) <> 8)
    THEN
        result := TO_DATE ('31122999', 'DDMMYYYY');
        RETURN result;
    END IF;

 --   dbms_output.put_line('before end');
    result:=TO_DATE (tmp_day || tmp_month ||tmp_year , 'DDMMYYYY');
 --   dbms_output.put_line('date result: '|| result);
    RETURN result;
EXCEPTION
    WHEN NO_DATA_FOUND
    THEN
        NULL;
    WHEN OTHERS
    THEN
        result := TO_DATE ('3012299', 'DDMMYYYY');
        RETURN result;
        RAISE;
END normalize_date;
Run Code Online (Sandbox Code Playgroud)

用法

SELECT customer_no,
       str_data_expirare,
       normalize_date (str_data_expirare_trim) AS data_expirare_buletin
  FROM (SELECT customer_no,
               str_data_expirare,
               REGEXP_REPLACE (str_data_expirare, '[^a-zA-Z0-9]+', '')
                   AS str_data_expirare_trim
          FROM (SELECT Q1.set_act_id_1,
                       Q1.customer_no,
                       NVL (SUBSTR (set_act_id_1,
                                      INSTR (set_act_id_1,
                                             '+',
                                             1,
                                             5)
                                    + 1,
                                    LENGTH (set_act_id_1)),
                            'NULL')
                           AS str_data_expirare
                  FROM STAGE_CORE.IFLEX_CUSTOMERS Q1
                  WHERE Q1.set_act_id_1 IS NOT NULL
                  )
        );
Run Code Online (Sandbox Code Playgroud)

APC*_*APC 5

如果您对所有可能的日期格式都有很好的了解,则使用蛮力可能会更容易:

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;
/
Run Code Online (Sandbox Code Playgroud)

请注意,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> 
Run Code Online (Sandbox Code Playgroud)

面对松散的数据完整性规则,这证明了自动数据清理的局限性。犯罪的工资是损坏的数据。


@AlexPoole提出了使用'RR'格式的问题。日期掩码的此元素是作为Y2K合并引入的。令人沮丧的是,我们距新千年已经过去了将近二十年。

无论如何,问题是这样的。如果我们将此字符串'161225'转换为日期,它具有什么世纪?好吧,'yymmdd'会给2016-12-15。足够公平,但是呢'991225'?我们真正想要的日期是2099-12-15多少?这是 'RR'格式发挥作用的地方。基本上,它默认世纪:数字00-49默认为20,50-99默认为19。该窗口由Y2K问题确定:在2000年,'98提及近期的可能性大于近期的可能性,并且类似的逻辑适用于'02。因此是1950年的中点。请注意,这是一个固定点,而不是滑动窗口。随着我们距2000年的发展越来越远,该枢轴点变得越没有用处。了解更多

不管怎样,关键的一点是,“RRRR”不与其他日期格式发挥很好:to_date('501212', 'rrrrmmdd') hurlsORA-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;
/
Run Code Online (Sandbox Code Playgroud)

关键点仍然是:在解释日期时,我们使此功能的智能程度是有限的,因此请确保您以最合适的方式进行领导。如果您认为大多数日期字符串都适合日-月-年,则将其放在第一位;您仍然会得到一些错误的演员表,但是如果您以年-月-日为首的话,您会得到更少的帮助。

  • 我猜您可以将FXFM添加到所有格式中以控制松弛,但是随后您需要更多的变化(开始时使用不同的标点符号),并且美国/欧洲格式之间仍然存在歧义性问题,然后还有日期语言。 ..仅需为最后一行加上+1 * 8-)只要您先检查4位数字,是否有理由不对两位数年份变化使用RR? (2认同)