【问题标题】:Receiving ORA-01843: not a valid month error while retrieving data between two dates接收 ORA-01843: 检索两个日期之间的数据时出现无效月份错误
【发布时间】:2021-01-29 21:17:02
【问题描述】:

我在 oracle 数据库表中有一个列 Varchar2。在此列中,我存储日期行 29-1-2021 或 28-12-2020。我想在两个日期之间从下面的查询中检索数据,然后我收到 Invalid month 的错误。我该如何解决这个问题?

SELECT Line_Stop_Id, Function_name, Product_family, line_description, Reason_Category, Reason_detail, 
  Product_item, product_description, request_raised_date, request_raised_time, cm.EMP_NAME as raised_by, Lse.User_Closer_Description,
  Cm1.Emp_Name as Closer_User, Lse.User_Closer_Date, Lse.Final_Closer_Description, Lse.Final_Closer_Date, Lse.Final_Closer_Time,
  Cm2.Emp_Name as Final_Closer, Lse.Resource_Effected ,
  ROUND(24*(sysdate - to_date(Request_Raised_Date
  ||' '
  ||request_raised_time, 'DD-Mon-RR HH24:MI:SS'))) AS TAT
  FROM Xx_Lsp_Linestoppage_Entry lse 
  Left join Emp_Master cm ON Lse.Raised_By = Cm.Emp_No
  Left join Emp_Master cm1 ON Lse.Closer_User = Cm1.Emp_No
  Left join Emp_Master cm2 ON Lse.Final_Closed_By = Cm2.Emp_No
  where TO_DATE(Lse.Request_Raised_Date, 'DD-Mon-RR') Between TO_DATE('01-Jan-21', 'DD-Mon-RR') and TO_DATE('29-Jan-21', 'DD-Mon-RR');

【问题讨论】:

  • 为什么将日期值存储为 varchar?这是一个非常非常糟糕的主意。每次你这样做,独角兽就会死去。您遇到的问题是该错误决定的直接结果(我敢打赌,有人说“是的,我们可以在以这种可怕的格式存储它们之前验证所有日期。相信我,不会有问题”)
  • 从不将日期存储为文本,始终使用正确的日期列。
  • 我不明白。您在表Xx_Lsp_Linestoppage_Entry 中写入该列Request_Raised_Date 包含一个表示日期的字符串,例如29-1-2021 但在您的 [SQL] 查询条件中,您使用的是不同的格式字符串,即 DD-Mon-RR。你熟悉 Oracle 的Datetime Format Models吗?
  • 也许 edit 您的问题并发布您收到的整个 [Oracle] 错误消息。
  • 难以置信,有些人一年仍然使用 2 位数 - 还是我应该说“有些人再次使用 2 位数”?也许Y2K-Bug 是太久以前了,人们已经不知道了。

标签: sql oracle


【解决方案1】:

您应该在转换之前检查存储为字符串的日期值的有效性,并清理它们(修复它们)。

这可以通过例如 PL/SQL 块中的游标来完成(或包装在函数中以导出查询结果或按其值过滤):

declare
  l_date date;
begin

  for r in (
    select distinct
      request_raised_time
    from Xx_Lsp_Linestoppage_Entry
    /*To reduce rows in cursor*/
    where not regexp_like('^\d{2}-\d{2}-\d{4}')
  ) loop
  
    begin
      
      l_date := to_date(r.q, 'dd-mm-yyyy');
    exception
      when others then dbms_output.put_line('Invalid date: ' || r.q);
    end;
  
  end loop;
end;
/

db小提琴here

注意,您需要在格式中引用破折号以使其准确,因为 Oracle 将不带引号的破折号视为来自相当广泛的分隔符集的任何符号。所以它会将01/12/2020 处理为date '2020-12-01',而不是作为无效日期。

select to_date('01/12/2021', 'dd-mm-yyyy') as dt from dual;

DT                   
-------------------- 
2021-12-01T00:00:00Z 


Elapsed: 00:00:00.002
1 rows selected.

select to_date('01-12-2021', 'dd-mm-yyyy') as dt from dual;

DT                   
-------------------- 
2021-12-01T00:00:00Z 


Elapsed: 00:00:00.002
1 rows selected.

select to_date('01$12$2021', 'dd-mm-yyyy') as dt from dual;

DT                   
-------------------- 
2021-12-01T00:00:00Z 


Elapsed: 00:00:00.002
1 rows selected.

【讨论】:

    【解决方案2】:

    您以29-1-2020 (dd-mm-yyyy) 格式存储日期,因此请在 to_Date 中使用此格式

    替换

    DD-Mon-RR
    

    dd-mm-yyyy
    

    【讨论】:

    • 感谢@Popeye 的回复。仍然收到同样的错误
    • 其中 TO_DATE(Lse.Request_Raised_Date, 'dd-mm-yyyy') 在 TO_DATE('01-01-2021', 'dd-mm-yyyy') 和 TO_DATE('29-01- 2021', 'dd-mm-yyyy');
    • 改格式,这里还有DD-Mon-RR HH24:MI:SS'
    • @MoinKhan Request_Raised_Date 列中的值是否包含前导空格或尾随空格或嵌入空格?例如:29 - 01 - 2021
    【解决方案3】:

    可能是 varchar2 类型的 Lse.Request_Raised_Date 字段,它包含的信息比您查询所需的信息多。 我认为最好的方法是选择一个字符串,该字符串只标识使用该函数识别日期所需的部分:

     substr(string , string start from , string lenght )
    

    这是一个例子:

     select to_date(substr('29-01-2021 23:01:55',1,10),'DD-MM-YYYY') from dual;
    
     TO_DATE (SU
     ----------
     29-01-2021
    

    代替我的字符串 '29 -01-2021 23:01:55' 输入字段 Lse.Request_Raised_Date

    【讨论】:

      【解决方案4】:

      你可以使用on conversion error:

      to_date(Request_Raised_Date || ' ' || request_raised_time 
              default null on conversion error, 'DD-Mon-YYYY HH24:MI:SS')
      

      这将返回 NULL 而不是错误。

      希望这是关于为什么不应该将日期/时间值存储为字符串的教训。您实际上可以使用以下方法查找错误值:

      select Request_Raised_Date, request_raised_time
      from Xx_Lsp_Linestoppage_Entry
      where to_date(Request_Raised_Date || ' ' || request_raised_time 
                  default null on conversion error, 'DD-Mon-YYYY HH24:MI:SS') is null and
            Request_Raised_Date is not null
      

      这可能会给您一些关于如何修复数据的想法。

      【讨论】:

        猜你喜欢
        • 2021-11-06
        • 1970-01-01
        • 2014-08-03
        • 1970-01-01
        • 1970-01-01
        • 2014-02-05
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多