我有以下查询,我试图在我正在处理的水晶报表中用作COMMAND。
SELECT * FROM myTable
WHERE to_date(myTable.sdate, 'MM/dd/yyyy') <= {?EndDate}
这样可以正常工作,但我唯一担心的是日期可能并不总是格式正确(由于用户错误)。我知道当to_date函数失败时会抛出异常..是否有可能以忽略SELECT语句中相应行的方式处理此异常?因为如果整个数据库中只有一个日期格式不正确,我的报告就会中断。
我查看了Oracle是否提供了isDate函数,但看起来您应该只处理异常。任何帮助将不胜感激。谢谢!
答案 0 :(得分:24)
回应Tony的评论,你最好不要在DATE列中存储日期,而不是强迫前端查询工具查找和处理这些例外。
但是,如果您遇到错误的数据模型,最简单的选择是创建一个执行转换并处理错误的函数,
CREATE OR REPLACE FUNCTION my_to_date( p_date_str IN VARCHAR2,
p_format_mask IN VARCHAR2 )
RETURN DATE
IS
l_date DATE;
BEGIN
l_date := to_date( p_date_str, p_format_mask );
RETURN l_date;
EXCEPTION
WHEN others THEN
RETURN null;
END my_to_date;
您的查询将成为
SELECT *
FROM myTable
WHERE my_to_date(myTable.sdate, 'MM/dd/yyyy') <= {?EndDate}
当然,您最有可能希望在MY_TO_DATE
调用中使用基于函数的索引,以便使此查询合理有效。
答案 1 :(得分:5)
如果您的数据不一致且存储为字符串的日期可能无效,那么您有3个选项。
我建议使用第一个选项,因为您的数据应该是一致的。
第二个选项将提供一些灵活性和速度,因为报告只会获取所需的行。
第三个选项将强制报告获取表中的每个记录,然后让报告过滤记录。
答案 2 :(得分:4)
我遇到了同样的问题......一个旧的遗留数据库,其中包含日期的varchar字段和该字段中数十年的错误数据。尽我所能,我也无法改变数据类型。但我提出了这个解决方案,以确定日期是否是最新的,这似乎也是你正在做的事情:
select * from MyTable
where regexp_like(sdate, '[0-1][0-9].[0-3][0-9].[0-9][0-9][0-9][0-9]')
-- make sure it's in the right format and ignore rows that are not
and substr(sdate,7,10) || substr(sdate,1,2) || substr(sdate,4,5) >= to_char({?EndDate}, 'YYYYMMDD')
-- put the date in ISO format and do a string compare
这种方法的好处是它不会像“2月30日”那样窒息。
答案 3 :(得分:2)
既然你说你对数据库“没有访问权限”,我假设你不能创建任何函数来帮助你,你只能运行查询吗?
如果是这种情况,那么以下代码可以通过以下警告为您提供所需的大部分内容: 1)您要评估的存储日期格式为“mm / dd / yyyy”。如果不是这种情况,那么您可以更改代码以适合您的格式。 2)数据库不包含无效日期,例如2月30日。
首先,我创建了测试表和测试数据:
create table test ( x number, sdate varchar2(20));
insert into test values (1, null);
insert into test values (2, '01/01/1999');
insert into test values (3, '1999/01/01');
insert into test values (4, '01-01-1999');
insert into test values (5, '01/01-1999');
insert into test values (6, '01-01/1999');
insert into test values (7, '12/31/1999');
insert into test values (8, '31/12/1999');
commit;
现在,查询:
WITH dates AS (
SELECT x
, sdate
, substr(sdate,1,2) as mm
, substr(sdate,4,2) as dd
, substr(sdate,7,4) as yyyy
FROM test
WHERE ( substr(sdate,1,2) IS NOT NAN -- make sure the first 2 characters are digits
AND to_number(substr(sdate,1,2)) between 1 and 12 -- and are between 0 and 12
AND substr(sdate,3,1) = '/' -- make sure the next character is a '/'
AND substr(sdate,4,2) IS NOT NAN -- make sure the next 2 are digits
AND to_number(substr(sdate,4,2)) between 1 and 31 -- and are between 0 and 31
AND substr(sdate,6,1) = '/' -- make sure the next character is a '/'
AND substr(sdate,7,4) IS NOT NAN -- make sure the next 4 are digits
AND to_number(substr(sdate,7,4)) between 1 and 9999 -- and are between 1 and 9999
)
)
SELECT x, sdate
FROM dates
WHERE to_date(mm||'/'||dd||'/'||yyyy,'mm/dd/yyyy') <= to_date('08/01/1999','mm/dd/yyyy');
我的结果:
X SDATE
- ----------
2 01/01/1999
WITH语句将执行大部分验证,以确保sdate值至少采用正确的格式。我不得不在每个单位月/日/年进行to_date评估,因为当我在sdate上执行to_date时,我仍然收到无效的月份错误。
我希望这会有所帮助。
答案 4 :(得分:1)
相信这个回复澄清了......
无效日期没有直接的EXCEPTION HANDLER。
下面给出一个简单的方法,一旦你知道像DD / MM / YYYY这样的格式,那么给定REGEXP_LIKE
以下的函数就像魅力一样。
to_date()
也可以,当找到invalid_date时,光标将转到OTHERS EXCEPTION
。如下所示。
DECLARE
tmpnum NUMBER; -- (1=true; 0 = false)
ov_errmsg LONG;
tmpdate DATE;
lv_date VARCHAR2 (15);
BEGIN
lv_date := '6/2/2018'; -- this will fail in *regexp_like* itself
lv_date := '06/22/2018'; -- this will fail in *to_date* and will be caught in *exception WHEN OTHERS* block
lv_date := '07/03/2018'; -- this will succeed
BEGIN
tmpnum := REGEXP_LIKE (lv_date, '[0-9]{2}/[0-9]{2}/[0-9]{4}');
IF tmpnum = 0
THEN -- (1=true; 0 = false)
ov_errmsg := '1. INVALID DATE FORMAT ';
DBMS_OUTPUT.PUT_LINE (ov_errmsg);
RETURN;
END IF;
tmpdate := TO_DATE (lv_date, 'DD/MM/RRRR');
--tmpdate := TRUNC (NVL (to_date(lv_date,'DD/MM/RRRR'), SYSDATE));
tmpnum := 1;
EXCEPTION
WHEN OTHERS
THEN
BEGIN
tmpnum := 0;
ov_errmsg := '2. INVALID DATE FORMAT ';
DBMS_OUTPUT.PUT_LINE (ov_errmsg || SQLERRM);
RETURN;
END;
-- continue with your other query blocks
END;
-- continue with your other query blocks
DBMS_OUTPUT.PUT_LINE (tmpnum);
END;
答案 5 :(得分:0)
从Oracle 12c开始,无需定义函数来捕获转换异常。
Oracle在TO_DATE
函数中引入了ON CONVERSION ERROR
子句。
基本上,该子句抑制了转换无效日期字符串时的错误(典型错误为ORA-01843, ORA-01841, ORA-011861, ORA-01840
),并返回指定的默认值或空值。
用法示例
select to_date('2020-99-01','yyyy-mm-dd') from dual;
-- ORA-01843: not a valid month
select to_date('2020-99-01' default null on conversion error,'yyyy-mm-dd') from dual;
-- returns NULL
select to_date('2020-99-01' default '2020-01-01' on conversion error,'yyyy-mm-dd') from dual;
-- 01.01.2020 00:00:00