2017-05-04 1 views
2

Ich möchte eine Zeichenfolge, die ein Datum enthält, zu einem einzelnen Formatdatum bringen. EX:oracle - Konvertieren Sie viele Datumsformate in ein einzelnes formatiertes Datum

  • 13-06-2012 bis 13-JUN-12
  • 13.06.2012 bis 13-Jun-12
  • 13-JUN-2012 bis 13-JUN-12
  • 13/jun-2012-13-JUN-12
  • ...

ich habe versucht, alle Sonderzeichen zu löschen und danach eine Funktion verwenden, die Zeichenfolge in ein einziges Format von Datum zu transformieren. Meine Funktion mehr Ausnahmen zurückzukehren, ich weiß nicht, warum ...

Die Funktion:

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; 

Nutzungs

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

Antwort

3

Wenn Sie eine solide Vorstellung aller möglichen Datumsformate es könnte einfacher sein brute force:

Beachten Sie, dass moderne Versionen von Oracle sind ziemlich verzeihend Witz h Datumsumwandlung. Diese Funktion behandelt Daten in Formate, die nicht in der Liste enthalten sind, mit einigen interessanten Konsequenzen:

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> 

, die die Grenzen der automatischen Datenbereinigung angesichts der laschen Datenintegrität Regeln demonstriert. Der Lohn der Sünde ist korrumpierte Daten.


@AlexPoole stellt sich die Frage der Verwendung des 'RR' Format. Dieses Element der Datumsmaske wurde als Y2K-Kludge eingeführt. Es ist bedrückend, dass wir es immer noch fast zwei Jahrzehnte nach Beginn des neuen Jahrtausends diskutieren.

Wie auch immer, das Problem ist das. Wenn wir diese Zeichenfolge '161225' zu einem Datum übertragen, welches Jahrhundert hat es? Nun, 'yymmdd' wird 2016-12-15 geben. Fair genug, aber was ist mit '991225'? Wie wahrscheinlich ist das Datum, das wir wirklich wollen, 2099-12-15? Hier kommt das Format 'RR' ins Spiel. Im Grunde ist das Jahrhundert voreingestellt: Die Zahlen 00-49 sind standardmäßig auf 20, 50-99 auf 19 voreingestellt. Dieses Fenster wurde durch das Jahr-2000-Problem bestimmt: Im Jahr 2000 war es wahrscheinlicher, dass '98 sich auf die jüngste Vergangenheit bezog als in naher Zukunft Logik angewendet auf '02. Daher die Halbzeit von 1950. Beachten Sie, das ist ein Fixpunkt kein Schiebefenster. Je weiter wir uns vom Jahr 2000 entfernen, desto weniger nützlich wird dieser Drehpunkt. Find out more.

Wie auch immer, der entscheidende Punkt ist, dass ‚RRRR‘ nicht gut mit anderen Datumsformaten spielt: to_date('501212', 'rrrrmmdd') hurls ora-01843: keine gültigen Monat . So, use ‚RR‘ and test for it before using ‚YYYY'`. So ist meine überarbeitete Funktion (mit einigen aufzuräumen) sieht wie folgt aus:

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

Der entscheidende Punkt bleibt: Es gibt eine Grenze, wie intelligent wir diese Funktion machen können, wenn es Daten zur Interpretation kommt, so stellen Sie sicher, dass Sie führen mit die beste Passform.Wenn du denkst, dass die meisten deiner Datumszeichenketten Tag-Monat-Jahr entsprechen, setze das zuerst; Sie werden immer noch einige falsche Würfe bekommen, aber weniger, wenn Sie mit Jahr-Monat-Tag führen.

+2

ich denke, man FXFM zu allen Formaten hinzufügen könnte die Laxheit zu kontrollieren, aber dann müssen Sie mehr Variationen (für unterschiedliche Zeichensetzung für einen Start), und Sie haben immer noch die Mehrdeutigkeit zwischen US/europäischen Formaten, und dann gibt es die Datumssprache ... Wort h a +1 nur für die letzte Zeile * 8-) Gibt es einen Grund, RR nicht für die zweistelligen Variationen zu verwenden, solange Sie die 4-stellige Nummer zuerst prüfen? –

+0

@AlexPoole - Messe über RR, obwohl es 2017 8- | – APC

2

Die String-to-Date Conversion Rules ermöglichen zusätzliche Formatierungsregeln (ohne dass andere Modifikatoren angewendet werden). (Siehe auch this question) Also:

  • MM auch MON und MONTH übereinstimmt;
  • MON entspricht auch MONTH (und umgekehrt);
  • YY entspricht auch YYYY;
  • RR entspricht auch RRRR; und
  • Die Interpunktion ist optional.

Was bedeutet, dass Sie tun können:

CREATE OR REPLACE FUNCTION parse_Date_String(
    in_string VARCHAR2 
) RETURN DATE DETERMINISTIC 
IS 
BEGIN 
    BEGIN 
    RETURN TO_DATE(in_string, 'DD-MM-YY'); 
    EXCEPTION 
    WHEN OTHERS THEN 
     NULL; 
    END; 
    BEGIN 
    RETURN TO_DATE(in_string, 'MM-DD-YY'); 
    EXCEPTION 
    WHEN OTHERS THEN 
     NULL; 
    END; 
    BEGIN 
    RETURN TO_DATE(in_string, 'YY-MM-DD'); 
    EXCEPTION 
    WHEN OTHERS THEN 
     NULL; 
    END; 
    RETURN NULL; 
END; 
/

Abfrage:

WITH dates (value) AS (
    SELECT '010101' FROM DUAL UNION ALL 
    SELECT '02JAN01' FROM DUAL UNION ALL 
    SELECT '03JANUARY01' FROM DUAL UNION ALL 
    SELECT '04012001' FROM DUAL UNION ALL 
    SELECT '05JAN2001' FROM DUAL UNION ALL 
    SELECT '06JANUARY2001' FROM DUAL UNION ALL 
    SELECT 'JAN0701' FROM DUAL UNION ALL 
    SELECT 'JANUARY0801' FROM DUAL UNION ALL 
    SELECT 'JAN0901' FROM DUAL UNION ALL 
    SELECT 'JANUARY1001' FROM DUAL UNION ALL 
    SELECT '990111' FROM DUAL UNION ALL 
    SELECT '99JAN12' FROM DUAL UNION ALL 
    SELECT '99JANUARY13' FROM DUAL UNION ALL 
    SELECT '19990114' FROM DUAL UNION ALL 
    SELECT '2001-01-15' FROM DUAL UNION ALL 
    SELECT '2001JAN16' FROM DUAL UNION ALL 
    SELECT '2001JANUARY17' FROM DUAL UNION ALL 
    SELECT '20010118' FROM DUAL 
) 
SELECT value, parse_Date_String(value) AS dt 
FROM dates; 

Ausgang:

VALUE   DT 
------------- ------------------- 
010101  2001-01-01 00:00:00 
02JAN01  2001-01-02 00:00:00 
03JANUARY01 2001-01-03 00:00:00 
04012001  2001-01-04 00:00:00 
05JAN2001  2001-01-05 00:00:00 
06JANUARY2001 2001-01-06 00:00:00 
JAN0701  2001-01-07 00:00:00 
JANUARY0801 2001-01-08 00:00:00 
JAN092001  2001-01-09 00:00:00 
JANUARY102001 2001-01-10 00:00:00 
990111  2099-01-11 00:00:00 
99JAN12  2099-01-12 00:00:00 
99JANUARY13 2099-01-13 00:00:00 
19990114  1999-01-14 00:00:00 
2001-01-15 2001-01-15 00:00:00 
2001JAN16  2001-01-16 00:00:00 
2001JANUARY17 2001-01-17 00:00:00 
20010118  0118-01-20 00:00:00 

(Hinweis: Die von Ihnen verwendeten Datumsformate sind nicht eindeutig, wie das letzte Beispiel zeigt. Sie können die Reihenfolge der Formate in der Funktion analysiert werden tauschen, um unterschiedliche Ergebnisse zu erhalten, aber wenn Sie 010203 haben, ist es 01-FEB-2003, 02-JAN-2003, 03-FEB-2001 oder sogar 01-FEB-0003?)

Wenn Sie es im Format wollen DD-MON-YY (aber warum YY ? und nicht YYYY), dann benutzen Sie einfach:

TO_CHAR(parse_Date_String(value), 'DD-MON-YY') 
Verwandte Themen