2011-05-11 4 views
6

내가 작업중인 크리스탈 보고서에서 COMMAND로 사용하려고하는 다음 쿼리가 있습니다.SELECT 문에서 to_date 예외를 처리하여 해당 행을 무시하는 방법은 무엇입니까?

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

이 경우에도 문제가 없지만 날짜가 항상 올바른 형식이 아닐 수 있습니다 (사용자 오류로 인해). 그 때 to_date 함수가 실패하면 예외를 throw합니다. 그것은 내 SELECT 문에서 해당 행을 무시하는 그런 식 으로이 예외를 처리 할 수 ​​있습니까? 그렇지 않으면 전체 데이터베이스에서 하나의 날짜 만 잘못 포맷 된 경우 보고서가 손상 될 수 있기 때문입니다.

오라클이 isDate 함수를 제공하는지 살펴 보았지만 예외를 처리해야하는 것처럼 보입니다. 어떤 도움이라도 대단히 감사하겠습니다. 감사!!

+2

DATE 열에 날짜를 저장하려면 _supposed_하십시오. 너 왜 그래? –

+0

내가 일하는이 레거시 응용 프로그램입니다. 그들은 문자열을 사용합니다. 그렇지 않으면 날짜가 현재 날짜로 기본 설정되어 있기 때문에 ... DB에 대한 제어권이 없습니다 – ntsue

+0

varchar2 필드에 날짜가 있고 일관된 형식이 없어도 SOL (모든 다양한 문자 형식을 실제 날짜 필드로 변환하기위한 테이블 재구성이 부족합니다.) – tbone

답변

22

Echoing 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 호출에 함수 기반 인덱스가 필요할 것입니다.

+0

up to best practice "레거시"(즉, 고장난) 시스템에 대한 픽스 플러스 –

+9

+1 레거시 시스템은 삶의 한 요소입니다. 우리는 가능한 한 최선을 다해야합니다. – APC

4

데이터가 일관되지 않고 문자열로 저장된 날짜가 유효하지 않은 경우 3 가지 옵션이 있습니다.

  1. 열이
  2. 저장 프로 시저
  3. A (복잡한) 레코드 선택에 날짜 문자열의 예외를 처리 날짜에 문자열의 예외를 처리 날짜 데이터 유형을 저장하고 있는지 확인하기 위해 DB를 리팩토링 공식

데이터가 일관성이 있어야하므로 첫 번째 옵션을 사용하는 것이 좋습니다.

두 번째 옵션은 보고서가 필요한 행만 가져 오므로 유연성과 속도를 제공합니다.

세 번째 옵션은 보고서가 테이블의 모든 레코드를 가져온 다음 레코드 아래로 보고서 필터를 갖도록합니다.

+0

응답을 보내 주셔서 감사합니다. 불행히도 데이터베이스에 대한 제어권이 없습니다 ... 그래, 옵션 3에 대해 생각해 보았습니다. 나는 오버 헤드를 지불하는 것을 원하지 않았다. 나는 2 번째로 조사 할 것이다 .. 나는 나의 ODBC와 저장 프로 시저로 과거에는 문제가 있었지만, 다시 검사 할 것이다. – ntsue

1

데이터베이스에 "액세스 할 수 없다"고 했으므로이 기능을 지원하는 기능을 만들 수없고 쿼리 만 실행할 수 있다고 가정합니다.

그런 경우 다음 코드는 다음주의 사항과 함께 필요한 것을 최대한 활용해야합니다. 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 값이 최소한 적절한 형식인지 확인해야합니다. 나는 toddate를 수행 할 때 여전히 유효하지 않은 월 오류가 발생했기 때문에 to_date 평가를 수행 할 때마다 월/일/년 단위로 나누어야했습니다.

이 정보가 도움이되기를 바랍니다.

2

동일한 문제가 있습니다 ... 필드에 날짜와 수십 년 동안 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 

이 방법의 장점이 월 "와 같은 날짜에 질식하지 않는있다 :하지만 날짜가 당신은뿐만 아니라 무엇을하고있는 것 같다, 이는 현재의 경우 찾기 위해이 솔루션을 함께했다 30 ".

0

신뢰할 수있는 답장을 신뢰할 수 있습니다. 유효하지 않은 날짜에 대한 직접 예외 핸들러가 없습니다. 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; 
관련 문제