简体   繁体   English

在将字符串转换为日期时,某些列值可能为null,这不是有效的月份Oracle查询

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

i have a query 我有一个查询

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

This query show error "Not a valid month" 此查询显示错误“不是有效月份”

if i apply same query on my other table where string date is not null it works fine. 如果我在我的另一个表上应用相同的查询,其中字符串日期不为null它工作正常。 This work fine 这工作很好

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

Assuming that DATE_ENTERED is a VARCHAR2 column 假设DATE_ENTEREDVARCHAR2

  1. You should never store a date as a string. 您不应该将日期存储为字符串。 A date should be stored in a DATE column. 日期应存储在DATE列中。 Using the wrong data type is the source of your problem and fixing the data model to use the correct data type is the proper solution. 使用错误的数据类型是问题的根源,修复数据模型以使用正确的数据类型是正确的解决方案。
  2. If you want to convert a string to a date, us the TO_DATE function with an explicit format mask. 如果要将字符串转换为日期,请使用带有显式格式掩码的TO_DATE函数。 A CAST or a TO_DATE with no format mask will cause whatever the session's NLS_DATE_FORMAT specifies as the format mask. 没有格式掩码的CASTTO_DATE将导致会话的NLS_DATE_FORMAT指定为格式掩码。 Since that can be different for every session, that means that your code may work for some users from some clients and not work for other users or other clients and the bugs will inevitably be hard to find. 由于每个会话可能会有所不同,这意味着您的代码可能适用于某些客户端的某些用户而不适用于其他用户或其他客户端,并且这些错误将不可避免地难以找到。
  3. A DATE should always be compared to another DATE not to a string. 应始终将DATE与另一个DATE进行比较,而不是与字符串进行比较。 Comparing a date to a string forces Oracle to do an implicit conversion which, again, uses the session's NLS_DATE_FORMAT and again makes your code unreliable. 将日期与字符串进行比较会强制Oracle执行隐式转换,再次使用会话的NLS_DATE_FORMAT并再次使您的代码不可靠。 If you want to specify literal dates, either use the TO_DATE function to convert the string to a date (with an explicit format mask) or use an ANSI date literal 如果要指定文字日期,请使用TO_DATE函数将字符串转换为日期(使用显式格式掩码)或使用ANSI日期文字
  4. Assuming that ID is the primary key, the DISTINCT is, at best, meaningless and, at worst, forcing Oracle to do an unnecessary sort. 假设ID是主键, DISTINCT充其量只是毫无意义,最坏的情况是迫使Oracle进行不必要的排序。

The first best solution is to modify the data model so that DATE_ENTERED is, in fact, a DATE . 第一个最佳解决方案是修改数据模型,以便DATE_ENTERED实际上是DATE If you do that, your query becomes (using ANSI date literals) 如果这样做,您的查询将变为(使用ANSI日期文字)

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

or with an explicit TO_DATE 或者使用明确的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' ) 

If, for some reason, you are stuck with the incorrect data model, if all the strings that you store are actually valid, you could do something like 如果由于某种原因,您遇到了错误的数据模型,如果您存储的所有字符串实际上都是有效的,那么您可以执行类似的操作

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' ) 

Given the error you are getting, however, it seems highly likely that there are at least some rows in the table where the string that has been stored in the table does not represent a valid date. 但是,鉴于您遇到的错误,表中至少有一些行很可能表中存储在表中的字符串不代表有效日期。 The problem then becomes trying to figure out which rows are invalid. 然后问题就是试图找出哪些行无效。 One option would be to create a new function 一种选择是创建一个新功能

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;

and then use that function in your query. 然后在查询中使用该函数。

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

will return all the rows where DATE_ENTERED does not represent a valid date in the format DD-Mon-YYYY . 将以DD-Mon-YYYY格式返回DATE_ENTERED不表示有效日期的所有行。 You'll have to correct this data eventually. 您最终必须更正此数据。 If you are OK with ignoring any row with invalid data, you can write the query 如果您可以忽略任何包含无效数据的行,则可以编写查询

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