首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在where子句中使用日期条件时的ORA-01841错误

问在where子句中使用日期条件时的ORA-01841错误
EN

Stack Overflow用户
提问于 2020-11-01 17:48:48
回答 3查看 688关注 0票数 0

我需要为客户挑选过期信用卡的数据。如您所知,卡的过期日期仅使用MM格式的月份和YY格式的年份。在我的数据库中,所有的过期日期都以MMYY格式存储,所以我使用TO_DATE(FONDOS.VENCTARJETA,'MMYY')获取日期,然后应用一些条件。

这是我的疑问:

代码语言:javascript
复制
SELECT 
    TO_DATE(FONDOS.VENCTARJETA, 'MMYY') AS F_VENCIMIENTO
FROM 
    POLIZA POLIZA,
    DATOS_FONDOSPOL FONDOS
WHERE
    POLIZA.IDEPOL = FONDOS.IDEPOL AND
    --TO_DATE(FONDOS.VENCTARJETA, 'MMYY') <= SYSDATE AND
    POLIZA.CODINTER = TO_NUMBER(:P2_CLAVE)

这将返回89行,如下所示:

代码语言:javascript
复制
F_VENCIMIENTO      |
-------------------|
                   |
2023-08-01 00:00:00|
2020-08-01 00:00:00|
2021-11-01 00:00:00|
2020-09-01 00:00:00|
                   |
2023-02-01 00:00:00|
---- many more ----

使用结果,我们注意到在将'MMYY‘日期转换为日期类型列时没有错误。

正如您所看到的,我在where子句中注释了一个条件,如果取消注释行,则得到以下内容:

ORA-01841:(完整)年份必须在-4713到+9999之间,而不是0

这不是我得到这个错误的唯一行为,但这是最简单的,我要向您展示它是如何失败的。

这没有逻辑,我找不到为什么会发生这种事。拜托救救我。谢谢。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2020-11-01 18:10:52

过滤器条件可以按照Oracle决定的最佳顺序执行。它建议您表中有一些行不能使用该特定格式掩码正确转换为日期,但确实会被联接条件过滤掉。当您包含过滤器时,Oracle可能会看到它可以在您的datos_fondospol表上预先筛选,然后再连接到您的另一个表,此时,每一行都会命中函数。

如果您使用的是至少OracleVersion12.2,则可以使用validate_conversion识别所有包含无法转换为带有该格式掩码的日期的数据的行

代码语言:javascript
复制
select 
from   datos_fondospol 
where  validate_conversion(venctarjeta as date, 'MMYY') = 0

如果该数据是正确的,但可以安全地忽略,那么您可以使用另一个12.2加法:

代码语言:javascript
复制
SELECT 
    TO_DATE(FONDOS.VENCTARJETA, 'MMYY') AS F_VENCIMIENTO
FROM 
    POLIZA POLIZA,
    DATOS_FONDOSPOL FONDOS
WHERE
    POLIZA.IDEPOL = FONDOS.IDEPOL AND
    TO_DATE(FONDOS.VENCTARJETA default null on conversion error, 'MMYY') <= SYSDATE AND
    POLIZA.CODINTER = TO_NUMBER(:P2_CLAVE)

如果您只在12.1上,那么您可以使用一个with plsql子句自己创建一个类似的函数:

代码语言:javascript
复制
with 
 function default_date(dateString varchar2,dateFormat varchar2)
 return date
 is
   convertedDate  date;
 begin
   convertedDate := to_date(dateString,dateFormat );
   return convertedDate ;
 exception when others then
   return null;
 end;
SELECT 
    TO_DATE(FONDOS.VENCTARJETA, 'MMYY') AS F_VENCIMIENTO
FROM 
    POLIZA POLIZA,
    DATOS_FONDOSPOL FONDOS
WHERE
    POLIZA.IDEPOL = FONDOS.IDEPOL AND
    default_date(FONDOS.VENCTARJETA, 'MMYY') <= SYSDATE AND
    POLIZA.CODINTER = TO_NUMBER(:P2_CLAVE)

如果小于这个值,则可以显式地执行PL/SQL函数并调用它。或者,您可以创建一个case表达式来首先检查字符串的内容。

代码语言:javascript
复制
SELECT 
    TO_DATE(FONDOS.VENCTARJETA, 'MMYY') AS F_VENCIMIENTO
FROM 
    POLIZA POLIZA,
    DATOS_FONDOSPOL FONDOS
WHERE
    POLIZA.IDEPOL = FONDOS.IDEPOL AND
    case when regexp_like (FONDOS.VENCTARJETA, '^[0-9]{4}$')
 and to_number(substr(FONDOS.VENCTARJETA,1,2)) between 1 and 12  
 then to_date(FONDOS.VENCTARJETA, 'MMYY') else cast(null as date) end <= SYSDATE AND
    POLIZA.CODINTER = TO_NUMBER(:P2_CLAVE)
票数 3
EN

Stack Overflow用户

发布于 2020-11-01 18:09:06

在VENCTARJETA列中,DATOS_FONDOSPOL中至少有一行字符串不能转换为有效日期。如果您查看带有和不带注释条件的查询计划,我的猜测是,当条件存在时,sysdate在CODINTER谓词之前和/或联接之前应用CODINTER谓词,并使用无效的数据过滤行。如果您的计划碰巧在应用to_date函数之前过滤掉了坏数据,那么查询就会看起来很有效。但是,如果有什么原因导致计划发生更改,并且在筛选出坏数据之前应用了to_date函数,那么您将得到一个错误。

票数 1
EN

Stack Overflow用户

发布于 2020-11-02 01:10:43

已经尝试过用例不确定这是否是您要寻找的

-用样本数据创建表

创建表POLIZA(IDPOL编号,VENCTARJETA日期);

创建表DATOS_FONDOSPOL(IDPOL编号,X_EXTRA varchar2(10));

插入POLIZA值(2001年,to_date('0816',‘MM/YY’);插入POLIZA值(2002年,to_date('1016','MM/YY'));

插入DATOS_FONDOSPOL值(2002年,'zzzz');插入DATOS_FONDOSPOL值(2001年,'zzzz');

下面的选择对我很好,我所做的就是使用TO_CHAR

代码语言:javascript
复制
SELECT 
   TO_CHAR(POLIZA.VENCTARJETA, 'MMYY')AS F_VENCIMIENTO
FROM 
    POLZIA POLIZA,
    DATOS_FONDOSPOL FONDOS
WHERE
    POLIZA.IDPOL = FONDOS.IDPOL 
    AND TO_CHAR(POLIZA.VENCTARJETA, 'MMYY') <= TO_CHAR(SYSDATE-120, 'MMYY');
票数 -1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/64635020

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档