我们如何只从 Oracle 中具有各种日期和字符串格式的列中提取日期?

How can we extract date only from a column with various date and string formats in Oracle?

ACTUAL expected
SEP-10-2017 10-SEP-2017
SEP 30 2018 30-SEP-2018
OFFICE OF SMALL
OCT-11-2018 11-OCT-2018
O9-SEP-2009 O9-SEP-2009
Not Applicable
NOV-20-2001 20-NOV-2001
BANIJYA BHIBAG
AUGUST 03 2017 03-AUG-2017
AUG-04-1991 04-AUG-1991
97/2015
09/09/2018 09-SEP-2018

如何才能得到如上的结果,并丢弃无法转换的日期?

一个选择是创建一个涵盖所有 种可能组合的函数。以下示例包含其中一些 - 您必须在找到它们时添加新的。

SQL> create or replace function f_date (par_str in varchar2)
  2    return date
  3  is
  4    l_date date;
  5  begin
  6    begin
  7      l_date := to_date(par_str, 'mon-dd-yyyy');
  8      return l_date;
  9    exception
 10      when others then null;
 11    end;
 12
 13    --
 14
 15    begin
 16      l_date := to_date(par_str, 'mon dd yyyy');
 17      return l_date;
 18    exception
 19      when others then null;
 20    end;
 21
 22    --
 23
 24    begin
 25      l_date := to_date(par_str, 'dd-mon-yyyy');
 26      return l_date;
 27    exception
 28      when others then null;
 29    end;
 30
 31    --
 32
 33    begin
 34      l_date := to_date(par_str, 'month dd yyyy');
 35      return l_date;
 36    exception
 37      when others then null;
 38    end;
 39
 40    --
 41
 42    begin
 43      l_date := to_date(par_str, 'dd/mm/yyyy');
 44      return l_date;
 45    exception
 46      when others then null;
 47    end;
 48
 49    return null;
 50
 51  exception
 52    when others then
 53      return null;
 54  end f_date;
 55  /

Function created.

测试:

SQL> select actual, f_date(actual) as expected
  2  from test;

ACTUAL          EXPECTED
--------------- -----------
SEP-10-2017     10-SEP-2017
SEP 30 2018     30-SEP-2018
OFFICE OF SMALL
09-SEP-2009     09-SEP-2009
Not applicable
AUGUST 03 2017  03-AUG-2017
97/2015
09/09/2018      09-SEP-2018

8 rows selected.

SQL>

可能出现的问题:如果actual = 09/10/12,什么是什么?

  • 09 可以是日、月或年
  • 10也可以是日月年
  • 12也可以是日月年

那么,它是什么?

  • 2012 年 10 月 9 日
  • 2012 年 9 月 10 日
  • 2009 年 10 月 12 日
  • ...

如果您确定您没有任何其他类型的格式可以转换为日期值(eg.all 数据符合此示例数据集) , 然后使用下面的代码块来填充另一个 table(t2) 与体面的日期值同时显示烂值如

SQL> SET SERVEROUTPUT ON
SQL> DECLARE
  dt DATE;
BEGIN
  FOR c IN (SELECT * FROM t) LOOP
    BEGIN
      dt := TO_DATE(c.col, 'MON-DD-YYYY', 'NLS_DATE_LANGUAGE=ENGLISH');
      INSERT INTO t2 VALUES(c.id,c.col);
    EXCEPTION
      WHEN OTHERS THEN
        BEGIN
          dt := TO_DATE(c.col, 'DD/MM/YYYY', 'NLS_DATE_LANGUAGE=ENGLISH');
          INSERT INTO t2 VALUES(c.id,c.col);
        EXCEPTION
          WHEN OTHERS THEN
            DBMS_OUTPUT.PUT_LINE(c.col || ' is not a valid date value');          
        END;
    END;
  END LOOP;
  COMMIT;
END;
/

Demo

从 Oracle 12 开始,您不需要 PL/SQL 并且可以使用:

SELECT actual,
       COALESCE(
         TO_DATE(
           actual DEFAULT NULL ON CONVERSION ERROR,
           'MM-DD-YYYY',
           'NLS_DATE_LANGUAGE=ENGLISH'
         ),
         TO_DATE(
           actual DEFAULT NULL ON CONVERSION ERROR,
           'DD-MON-YYYY',
           'NLS_DATE_LANGUAGE=ENGLISH'
         )
       ) AS parsed
FROM   table_name;

其中,对于示例数据:

CREATE TABLE table_name (ACTUAL) AS
SELECT 'SEP-10-2017'     FROM DUAL UNION ALL
SELECT 'SEP 30 2018'     FROM DUAL UNION ALL
SELECT 'OFFICE OF SMALL' FROM DUAL UNION ALL
SELECT 'OCT-11-2018'     FROM DUAL UNION ALL
SELECT 'O9-SEP-2009'     FROM DUAL UNION ALL
SELECT 'Not Applicable'  FROM DUAL UNION ALL
SELECT 'NOV-20-2001'     FROM DUAL UNION ALL
SELECT 'BANIJYA BHIBAG'  FROM DUAL UNION ALL
SELECT 'AUGUST 03 2017'  FROM DUAL UNION ALL
SELECT 'AUG-04-1991'     FROM DUAL UNION ALL
SELECT '97/2015'         FROM DUAL UNION ALL
SELECT '09/09/2018'      FROM DUAL;

输出:

ACTUAL PARSED
SEP-10-2017 10-SEP-2017
SEP 30 2018 30-SEP-2018
OFFICE OF SMALL
OCT-11-2018 11-OCT-2018
O9-SEP-2009
Not Applicable
NOV-20-2001 20-NOV-2001
BANIJYA BHIBAG
AUGUST 03 2017 03-AUG-2017
AUG-04-1991 04-AUG-1991
97/2015
09/09/2018 09-SEP-2018

注意:O9-SEP-2009 尚未解析,因为您的第一个字符是字母 O 而不是数字 0

db<>fiddle here