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

时间:2013-04-12 06:06:40

标签: sql date oracle11g

我有一个查询

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'

1 个答案:

答案 0 :(得分:2)

假设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进行不必要的排序。
  5. 第一个最佳解决方案是修改数据模型,以便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
    

    将以DATE_ENTERED格式返回DD-Mon-YYYY不代表有效日期的所有行。您最终必须更正此数据。如果您可以忽略任何包含无效数据的行,则可以编写查询

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