【问题标题】:Oracle Date column cleaningOracle 日期列清洗
【发布时间】:2010-11-02 13:11:34
【问题描述】:

我有一个 table1(DueDate varchar2(20))。它有数以千计的不同格式的日期数据和一些像字符这样的坏数据。

例如。

YYYYMMDD,
MM/DD/YYYY,
MM-DD-YYYY,
M/D/YYYY,
'ABCD'
YYYYMMD,
YYYYMDD,

现在我必须得到逾期一周的日期。我该怎么做?

感谢您的帮助。

【问题讨论】:

  • 贾斯汀的回答为您提供了一个很好的过程,但您将面临解释诸如“1/4/2010”之类的文本的数据完整性问题 - 2010 年 4 月 1 日或 2010 年 1 月 4 日?
  • @Nick;在所示示例中,没有日期格式的日期在一个月之前。尽管如此,112 是格式 MMD 还是 MDD 之间的区别将是一个问题。结论:存储在此列中的数据已经损坏,您还需要人工 - 无需计算机 - 干预来解决此问题。

标签: oracle


【解决方案1】:

这是在字符字段中存储日期信息是个坏主意的原因之一。

最简单的选择是创建一个函数,该函数尝试使用您拥有的任何优先级顺序(即 010203 Jan 2, 2003 或 Feb 3, 2001 或其他)格式将字符串转换为日期并捕获异常.类似的东西

CREATE OR REPLACE FUNCTION my_to_date( p_str IN VARCHAR2 )
  RETURN DATE
IS
  l_date DATE;
BEGIN
  l_date := to_date( p_str, 'YYYYMMDD' );
  RETURN l_date;
EXCEPTION
  WHEN others THEN
    BEGIN
      l_date := to_date( p_str, 'MM/DD/YYYY' );
      RETURN l_date;
    EXCEPTION 
      WHEN others
      THEN
        RETURN null;
    END;
END;

类似

SQL> CREATE OR REPLACE FUNCTION my_to_date( p_str IN VARCHAR2 )
  2    RETURN DATE
  3  IS
  4    l_date DATE;
  5  BEGIN
  6    l_date := to_date( p_str, 'YYYYMMDD' );
  7    RETURN l_date;
  8  EXCEPTION
  9    WHEN others THEN
 10      BEGIN
 11        l_date := to_date( p_str, 'MM/DD/YYYY' );
 12        RETURN l_date;
 13      EXCEPTION
 14        WHEN others
 15        THEN
 16          RETURN null;
 17      END;
 18  END;
 19  /

Function created.

SQL> select my_to_date( '19000101' ) from dual;

MY_TO_DAT
---------
01-JAN-00

SQL> ed
Wrote file afiedt.buf

  1* select my_to_date( '01/02/2005' ) from dual
SQL> /

MY_TO_DAT
---------
02-JAN-05

当然,您必须在代码中编写完整的有效日期格式,我只处理您列表中的前两个。

【讨论】:

  • +1 return 语句可以执行 to_date 本身,这样就不必定义 l_date。
【解决方案2】:

这个答案建立在 Justin Cave 的基础上。如果您创建另一个作为日期字段的列,则可以执行以下操作:

UPDATE table1 SET DueDate2 = my_to_date(DueDate) 
   WHERE DueDate2 IS NULL AND DueDate IS NOT NULL;

您可以在每次修改函数中的日期格式后重新运行此更新。当所有未转换的日期都是无效日期(例如“ABCD”)时,您将不得不停止。

【讨论】:

    【解决方案3】:

    适用于 Oracle 12.2 的纯 SQL 解决方案,其中方便的 default null on conversion error 选项避免了编写函数和处理异常的需要:

    with demo (date_string) as
         ( select '2018081' from dual union all
           select '01/8/2018' from dual union all
           select '01-8-2018' from dual union all
           select 'ABCD' from dual union all
           select '2018-08-01' from dual union all
           select '2018-01-8' from dual union all
           select '2017081' from dual union all
           select '01-AUG-2018' from dual
         )
    select t.date_string
         , coalesce
           ( to_date(t.date_string default null on conversion error)
           , case sys_context('USERENV','NLS_TERRITORY')
                  when 'AMERICA' then to_date(t.date_string default null on conversion error,'MM/DD/YYYY')
                  else to_date(t.date_string default null on conversion error,'DD/MM/YYYY')
             end
           , to_date(t.date_string default null on conversion error,'DD/MM/YYYY')
           , to_date(t.date_string default null on conversion error,'YYYY/MM/DD')
           , to_date(t.date_string default null on conversion error,'YYYYMMDD')
           , to_date(t.date_string default null on conversion error,'DD/MON/YYYY')
           ) as converted_date
    from   demo t
    

    结果:

    DATE_STRING CONVERTED_DATE
    ----------- --------------
    2018081     01-AUG-2018
    01/8/2018   08-JAN-2018
    01-8-2018   08-JAN-2018
    ABCD
    2018-08-01  01-AUG-2018
    2018-01-8   08-JAN-2018
    2017081     01-AUG-2017
    01-AUG-2018 01-AUG-2018
    

    第一个测试没有指定任何格式以使用会话默认值(DD-MON-RRRR 等)。

    下一个测试尝试处理北美将月份放在首位的习惯。 (我不确定sys_context('USERENV','NLS_TERRITORY') = 'AMERICA' 是否是一个可靠的测试,因为无论实际区域如何,这往往是默认设置。但是它说明了如何在需要时注入一些条件逻辑。)

    to_date 对分隔符非常宽容,所以我没有为 DD-MON-YYYYDD/MON/YYYY 等指定不同的格式。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-07-24
      • 1970-01-01
      • 2020-11-27
      • 2017-11-01
      • 2021-02-04
      • 2019-03-15
      相关资源
      最近更新 更多