2011-05-11 33 views
6

我有以下查詢,我正嘗試在我正在使用的水晶報表中用作COMMAND。如何在SELECT語句中處理to_date異常以忽略這些行?

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

這工作得很好,但是我唯一擔心的是,日期可能不總是正確的格式(由於用戶的錯誤)。我知道,當to_date函數失敗時,它會拋出一個異常..是否有可能以這種方式處理這個異常,使得它忽略了SELECT語句中的相應行?因爲否則如果整個數據庫中只有一個日期的格式不正確,我的報告會中斷。

我期待看看Oracle是否提供了isDate函數,但看起來你應該只是處理異常。任何幫助將不勝感激。謝謝!!

+2

那麼你是_supposed_將日期存儲在DATE列中。你爲什麼不呢? – 2011-05-11 14:54:44

+0

這是我在工作中使用的遺留應用程序。他們使用字符串,因爲否則日期默認爲當前日期...我無法控制數據庫 – ntsue 2011-05-11 15:04:17

+0

,如果你有varchar2字段中的日期(壞),你甚至沒有一致的格式,那麼你可能是SOL(短缺的重建表格將所有各種字符格式轉換爲實際的日期字段) – tbone 2011-05-11 17:43:00

回答

22

迴應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

被評爲最佳實踐加上針對「遺留」(即損壞)系統的修復 – 2011-05-11 15:33:41

+9

+1遺留系統是生活中的事實。我們必須盡我們所能處理它們。 – APC 2011-05-11 17:48:42

4

如果您的數據不一致,並且以字符串形式存儲的日期可能無效,那麼您有3個選項。

  1. 重構你的數據庫,以確保列中存儲的日期數據類型
  2. 處理字符串到目前爲止的例外在存儲過程中
  3. 處理字符串到目前爲止的例外(復)記錄選擇公式

我會建議使用第一個選項,因爲您的數據應該一致。

第二個選項將提供一些靈活性和速度,因爲報告只會獲取所需的行。

第三個選項將強制報表獲取表中的每條記錄,然後讓報表篩選記錄。

+0

嘿,謝謝你的迴應.. 不幸的是,我無法控制數據庫... 是啊,我正在考慮選項3 ..但我不想支付涉及的開銷.. 我會先看看2 ..我有過去的問題,我的ODBC和存儲過程..但我會再次檢查它 – ntsue 2011-05-11 15:04:47

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值至少具有適當的格式。由於我在sdate上執行to_date時仍然收到無效的月份錯誤,因此我不得不每隔一個單位月/日/年進行一次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

相信這個回覆澄清... 沒有直接的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;