簡體   English   中英

在將字符串轉換為日期時,某些列值可能為null,這不是有效的月份Oracle查詢

[英]Not a valid month-Oracle query when casting string to date where some column value may be null

我有一個查詢

SELECT DISTINCT ID 
  From ACCUSED 
 WHERE CAST(DATE_ENTERED AS date) BETWEEN '12-Apr-2013' and '12-Apr-2013'

此查詢顯示錯誤“不是有效月份”

如果我在我的另一個表上應用相同的查詢,其中字符串日期不為null它工作正常。 這工作很好

SELECT DISTINCT ID 
  From Customer 
 WHERE CAST(DATE_ENTERED AS date) BETWEEN '12-Apr-2013' and '12-Apr-2013'

假設DATE_ENTEREDVARCHAR2

  1. 您不應該將日期存儲為字符串。 日期應存儲在DATE列中。 使用錯誤的數據類型是問題的根源,修復數據模型以使用正確的數據類型是正確的解決方案。
  2. 如果要將字符串轉換為日期,請使用帶有顯式格式掩碼的TO_DATE函數。 沒有格式掩碼的CASTTO_DATE將導致會話的NLS_DATE_FORMAT指定為格式掩碼。 由於每個會話可能會有所不同,這意味着您的代碼可能適用於某些客戶端的某些用戶而不適用於其他用戶或其他客戶端,並且這些錯誤將不可避免地難以找到。
  3. 應始終將DATE與另一個DATE進行比較,而不是與字符串進行比較。 將日期與字符串進行比較會強制Oracle執行隱式轉換,再次使用會話的NLS_DATE_FORMAT並再次使您的代碼不可靠。 如果要指定文字日期,請使用TO_DATE函數將字符串轉換為日期(使用顯式格式掩碼)或使用ANSI日期文字
  4. 假設ID是主鍵, DISTINCT充其量只是毫無意義,最壞的情況是迫使Oracle進行不必要的排序。

第一個最佳解決方案是修改數據模型,以便DATE_ENTERED實際上是DATE 如果這樣做,您的查詢將變為(使用ANSI日期文字)

SELECT id
  FROM accused
 WHERE date_entered BETWEEN date '2013-04-12' and date '2013-04-12'

或者使用明確的TO_DATE

SELECT id
  FROM accused
 WHERE date_entered BETWEEN to_date( '12-Apr-2013', 'DD-Mon-YYYY' ) 
                        AND to_date( '12-Apr-2013', 'DD-Mon-YYYY' ) 

如果由於某種原因,您遇到了錯誤的數據模型,如果您存儲的所有字符串實際上都是有效的,那么您可以執行類似的操作

SELECT id
  FROM accused
 WHERE to_date( date_entered, 'DD-Mon-YYYY' ) BETWEEN to_date( '12-Apr-2013', 'DD-Mon-YYYY' ) 
                                                  AND to_date( '12-Apr-2013', 'DD-Mon-YYYY' ) 

但是,鑒於您遇到的錯誤,表中至少有一些行很可能表中存儲在表中的字符串不代表有效日期。 然后問題就是試圖找出哪些行無效。 一種選擇是創建一個新功能

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;

然后在查詢中使用該函數。

SELECT *
  FROM accused
 WHERE date_entered IS NOT NULL
   AND my_to_date( date_entered, 'DD-Mon-YYYY' ) IS NULL

將以DD-Mon-YYYY格式返回DATE_ENTERED不表示有效日期的所有行。 您最終必須更正此數據。 如果您可以忽略任何包含無效數據的行,則可以編寫查詢

SELECT id
  FROM accused
 WHERE my_to_date( date_entered, 'DD-Mon-YYYY' ) BETWEEN to_date( '12-Apr-2013', 'DD-Mon-YYYY' ) 
                                                     AND to_date( '12-Apr-2013', 'DD-Mon-YYYY' ) 

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM