如何处理SELECT语句中的to_date异常以忽略这些行?

时间:2011-05-11 14:52:41

标签: oracle crystal-reports

我有以下查询,我试图在我正在处理的水晶报表中用作COMMAND。

SELECT * FROM myTable
WHERE to_date(myTable.sdate, 'MM/dd/yyyy') <= {?EndDate}

这样可以正常工作,但我唯一担心的是日期可能并不总是格式正确(由于用户错误)。我知道当to_date函数失败时会抛出异常..是否有可能以忽略SELECT语句中相应行的方式处理此异常?因为如果整个数据库中只有一个日期格式不正确,我的报告就会中断。

我查看了Oracle是否提供了isDate函数,但看起来您应该只处理异常。任何帮助将不胜感激。谢谢!

6 个答案:

答案 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个选项。

  1. 重构数据库以确保该列存储日期数据类型
  2. 处理存储过程中迄今为止的字符串异常
  3. 处理(复杂)记录选择公式中的字符串异常
  4. 我建议使用第一个选项,因为您的数据应该是一致的。

    第二个选项将提供一些灵活性和速度,因为报告只会获取所需的行。

    第三个选项将强制报告获取表中的每个记录,然后让报告过滤记录。

答案 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