我想将包含日期的字符串带到单个格式的日期。 EX:
我尝试删除所有特殊字符,之后使用函数将该字符串转换为单一格式的日期。我的函数返回更多异常,我不知道为什么......
功能:
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;
用法
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
)
);
答案 0 :(得分:4)
如果您对所有可能的日期格式有充分的了解,那么使用暴力可能会更容易:
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;
/
请注意,现代版本的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>
这表明面对宽松的数据完整性规则时自动数据清理的局限性。罪的工资是腐败的数据。
@AlexPoole提出了使用'RR'
格式的问题。日期掩码的这个元素是作为Y2K kludge引入的。相当令人沮丧的是,我们在新的千禧年近二十年仍在讨论它。
无论如何,问题是这个。如果我们将这个字符串'161225'
转换为具有它的世纪的日期?好吧,'yymmdd'
会给2016-12-15
。很公平,但'991225'
呢?我们真正想要的日期是2099-12-15
的可能性有多大?这是'RR'
格式发挥作用的地方。基本上它默认为世纪:数字00-49默认为20,50-99默认为19.此窗口由Y2K问题决定:在2000年,'98
更有可能提到最近的过去而不是近未来,以及适用于'02
的类似逻辑。因此1950年的中间点。注意这是固定点而不是滑动窗口。随着我们从2000年开始进一步发展,支点变得越来越有用。 Find out more
无论如何,关键点是'RRRR'与其他日期格式不一致:to_date('501212', 'rrrrmmdd') hurls
ora-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;
/
关键点仍然是:在解释日期方面,我们能够将这项功能变得多么聪明,因此请确保以最合适的方式领先。如果你认为你的大多数日期字符串都符合日 - 月 - 年的要求;你仍然会得到一些错误的演员阵容,但如果你带领一年一天的话会少一些。
答案 1 :(得分:2)
String-to-Date Conversion Rules允许其他格式规则(不应用任何其他修饰符)。 (另见this question)所以:
MM
也匹配MON
和MONTH
; MON
也匹配MONTH
(反之亦然); YY
也匹配YYYY
; RR
也匹配RRRR
;和这意味着你可以做到:
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;
/
<强>查询强>:
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;
<强>输出强>:
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
(注意:您使用的日期格式不明确,如最后一个示例所示。您可以交换函数中解析格式的顺序以获得不同的结果,但如果您有010203
则它是01-FEB-2003
,02-JAN-2003
,03-FEB-2001
还是01-FEB-0003
?)
如果您希望采用DD-MON-YY
格式(但为什么YY
而不是YYYY
?),那么只需使用:
TO_CHAR( parse_Date_String( value ), 'DD-MON-YY' )