【问题标题】:Oracle Date Error - ORA-01841Oracle 日期错误 - ORA-01841
【发布时间】:2015-05-03 23:43:32
【问题描述】:

我在Oracle11g中有下表。

SQL> DESC tmp_test;
Name                 Type          Nullable Default Comments 
-------------------- ------------- -------- ------- -------- 
SERNO                NUMBER(10)                              
CARDNO               VARCHAR2(25)  Y                         
COL_A                VARCHAR2(255) Y                         
DATEA                DATE          Y                         
DATEB                DATE          Y                         
TAG                  VARCHAR2(255) Y                         
FEEDBACK             CHAR(1)       Y


SQL> 
SQL> SELECT * FROM (SELECT T.COL_A FROM TEMP_TEST T ORDER BY DBMS_RANDOM.VALUE) WHERE ROWNUM <=10;
COL_A
--------------------------------------------------------------------------------
00 OK.20150301-0000
00 OK.20150301-0000
00 OK.20150301-0000
00 OK.20150205-0000
00 OK.20150301-0000
00 OK.20150301-0000
00 OK.20150213-0000
00 OK.20150301-0000
00 OK.20150129-0000
00 OK.20150301-0000
10 rows selected

SQL>

我正在尝试识别表 TEMP_TEST 中 COL_A 中的 DATE 小于 SYSDATE - 7 的所有行。

SQL>
SQL> SELECT * FROM TEMP_TEST T WHERE
TO_DATE(SUBSTR(TRIM(T.COL_A),7,8),'YYYYMMDD') < sysdate-7;
**ORA-01841: (full) year must be between -4713 and +9999, and not be 0**
SQL>

该表仅包含 200 行,因此我目视检查了数据是否存在任何问题。所有日期均有效。这个错误的原因可能是什么?

谢谢

【问题讨论】:

  • 您可以尝试将nls_date_language 添加到to_date 吗?试试这个SELECT TO_DATE(SUBSTR(TRIM('00 OK.20150301-0000'),7,8),'YYYYMMDD', 'NLS_DATE_LANGUAGE = AMERICAN') FROM dual;
  • 如果我是你,我会做select SUBSTR(TRIM(T.COL_A),7,8),'YYYYMMDD' from temp_test 然后检查返回的内容。通常这足以识别有问题的行。
  • SELECT TO_DATE(SUBSTR(TRIM('00 OK.20150301-0000'),7,8),'YYYYMMDD', 'NLS_DATE_LANGUAGE = AMERICAN') FROM dual;已选择 1 行。 OK select SUBSTR(TRIM(T.COL_A),7,8),'YYYYMMDD' from temp_test -- 全部 270 行被选中。

标签: oracle substr to-date sysdate


【解决方案1】:

显然所有日期都无效,因此您遇到了错误。我会尝试以下类似的方法(未经测试,但认为没问题),只是为了识别问题记录。

declare 
  v_date date;
begin
  for c in (select col_a from temp_test) loop
    begin
      v_date := to_date(substr(trim(c.col_a),7,8),'YYYYMMDD');
    exception when others then
      dbms_output.put_line(c.col_a);
    end;
  end loop;
end;

请注意,这种使用others 作为唯一异常处理程序的做法通常被认为是不好的做法。在生产代码中,应单独处理异常。即使用于调试,最好输出 SQL 错误,但对于预期只有少数错误的第一次通过,有时偷懒也可以。

【讨论】:

  • 当其他人将捕获所有错误时,您应该捕获ORA-01841。那么为什么otherswhen others 本身就是一个错误。
  • 就像我说的,它很懒惰。我不会在生产代码中这样做。在这种情况下,我们知道我们正在处理日期转换错误,并且我们知道我们最多需要检查 200 个值。如果有的话,在这里单独处理所有可能的异常将是迂腐的。
  • 感谢添加那行,我只是觉得盲目复制代码的人应该知道它的用途。我也评论了另一个答案。很有建设性,感谢您的回复。
  • 谢谢拉利特,你说的很对 - 我会相应地修改我的答案。
  • 现在是完整的答案,+1
【解决方案2】:

使用显式游标来调试数字或日期转换错误通常会很快找出有问题的行(在这里,我伪造了您的 TEMP_TEST 表):

declare
  cursor l_cur is
    with temp_test(col_a) as (
      select '20150201_abc' col_a from dual union all
      select 'x0150201_abc' col_a from dual union all
      select '20150201_abc' col_a from dual)      
    SELECT * FROM TEMP_TEST T;

  l_data l_cur%rowtype;
  dummy  date;
begin
  open l_cur;
  loop
    fetch l_cur
      into l_data;
    exit when l_cur%notfound;
    begin
      dummy := TO_DATE(SUBSTR(TRIM(l_data.COL_A), 1, 8), 'YYYYMMDD');
    exception
      when others then
        dbms_output.put_line(sqlerrm || ' for ' || l_data.col_a);
    end;
  end loop;
end;

编辑:WHEN OTHERS 仅用于交互式调试目的 - 请不要在生产代码中使用它。

【讨论】:

  • 当其他人将捕获所有错误时,您应该捕获ORA-01841。那么为什么otherswhen others 本身就是一个错误。
  • 修改了光标。光标 l_cur 是从 TEMP_TEST 中选择 COL_A;未提取任何行。通过使用 SQL SELECT TO_DATE(SUBSTR(TRIM(T.COL_A),7,8),'YYYYMMDD') FROM TEMP_TEST T 提取所有行来确认我的数据没有问题
  • 这是一个仅用于交互式调试目的的脚本。在生产代码中使用WHEN OTHERS 几乎总是一个错误(但在某些情况下它是有意义的,例如记录错误并重新引发它)。
  • Lalit,我接受你对我的回答的批评,尽管在这种情况下我不同意。我完全不接受这个答案。 others 的使用在这里完全有效,因为它与输出 sqlerrm 一起使用以帮助调试。
  • 弗兰克,首先我希望你没有误会我的意思,我只是添加了那个评论,这样没有人会盲目地复制你的代码并使用它,至少通过阅读那个评论然后阅读你的回复会一些意义。这一切都是建设性的。所以,感谢您的回复,这让整个评论的事情有了完整的意义。非常感谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-09-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-10
相关资源
最近更新 更多