繁体   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