ORA-01841:(完整)年份必须介于 -4713 和 +9999 之间,并且不能为 0 提示

Posted

技术标签:

【中文标题】ORA-01841:(完整)年份必须介于 -4713 和 +9999 之间,并且不能为 0 提示【英文标题】:ORA-01841: (full) year must be between -4713 and +9999, and not be 0 tips 【发布时间】:2020-12-22 16:14:29 【问题描述】:

我想对表 test_tbl 的列 DATUM 中大于 01.01.2020 的所有值进行分组。当我运行查询时:

SELECT to_date("DATUM", 'YYYYMM')
FROM test_tbl
WHERE to_date("DATUM", 'YYYYMM') >= to_date('2020-01-01' ,'YYYY-MM-DD')
GROUP BY to_date("DATUM", 'YYYYMM')

我收到以下错误

ORA-01841:(完整)年份必须介于 -4713 和 +9999 之间,并且不能为 0。

表格test_tbl 看起来像:

DATUM (varchar2)
201701
202001
201901
201801
202003

当我只运行没有 WHERE CLAUSE 的 GROUP BY 时,日期转换有效,并且没有 NULL 值或类似的东西

SELECT to_date("DATUM", 'YYYYMM')
FROM test_tbl
GROUP BY to_date("DATUM", 'YYYYMM')

`

【问题讨论】:

请用英文转发。 DATUM的数据类型是什么?如果它不是 VARCHAR2,那么您不应该使用 TO_DATE,它将 varchar2 作为其第一个输入参数。 它的 VARCHAR2,当我将列转换为 DATE 数据类型时,我也会收到错误 DATUM的数据类型是什么?如果它是VARCHAR2 或数字,那么:为什么要将数据值存储为字符串,请使用正确的DATE 格式。如果它是 DATE,那么:永远不要在已经是 DATE 的值上调用 TO_DATE。请详细说明您的问题。 测试数据真的只有5行吗?我问是因为我能够创建您的表、添加 5 行并毫无问题地运行您的查询。结果是 2020-01-01 和 2020-03-01。另外,这里 GROUP BY 的目的是什么? 【参考方案1】:

这是一个数据错误。在 DATUM 列的某处,您有一个无法转换为日期的字符串。当我们将数据存储为错误的数据类型时,这总是存在风险。

如果您使用的是 Oracle 12c R2 或更高版本,您可以使用如下查询轻松定位错误行:

select * from your_table
where validate_conversion(datum as date, 'yyyymm') = 0

如果您使用的是早期版本的数据库,您可以创建一个执行类似操作的函数......

create or replace function is_date(p_str  in varchar2
                                  ,p_mask in varchar2 := 'yyyymm' ) return number is
  n pls_integer;
begin
  declare
    dt date;
  begin
    dt := to_date(p_str, p_mask);
    n := 1;
  exception
    when others then
      n := 0;
  end;
  return n;
end;
/

validate_conversion() 这样返回 1 表示有效日期,0 表示无效日期。

select * from your_table
where is_date(datum, 'yyyymm') = 0

这种方法更安全,因为它应用了 Oracle 的实际日期验证机制。然而,使用模式匹配正则表达式等让我们对传递无法转换为日期的字符串的弱模式开放。

【讨论】:

++ 我同意 VALIDATE_CONVERSION 是一个更优雅的解决方案。 Validate_Conversion 正在工作,但我只得到一个空结果。所以这意味着它只返回“1”的有效日期。我还能做什么? 第二个解决方案也导致空结果【参考方案2】:

您的数据列只需要包含数字数据,以便以您指定的格式作为日期处理。加上最后两位数字需要介于 01 和 12 之间;但这是一个单独的问题,因为您的错误消息是抱怨年份的值。

因此您可以使用以下方法检查无效值:

    SELECT
     datum
    ,LENGTH(REGEXP_REPLACE(datum,'[0-9]','')) as char_count
FROM test_tbl
WHERE LENGTH(REGEXP_REPLACE(datum,'[0-9]','')) > 0
;

应该很快地通过 300k 行。

【讨论】:

使用 WHERE LENGTH(REGEXP_REPLACE(datum,'[0-9]','')) > 0 可能是最慢的解决方案之一。

以上是关于ORA-01841:(完整)年份必须介于 -4713 和 +9999 之间,并且不能为 0 提示的主要内容,如果未能解决你的问题,请参考以下文章

ORA-01841 :(full) year 必须在 -4713 和 +9999 之间,并且在使用 DBMS_SQL 绑定到动态 sql 时不能为 0

错误 ORA-01841:在文件中传递出生日期时

oracle 全年度值没有为零 为啥还出 ora-01841

完整(四位数)年份或最后两位数的年份更新,使用JavaScript

SQL查询选择年份在两个日期之间的行

01847. 00000 - “日期必须介于 1 和最后一天之间”