【问题标题】:Why ORACLE TO_DATE function does not gives an ORA-01841 error in OR condition when spaces gets passed?为什么 ORACLE TO_DATE 函数在传递空格时不会在 OR 条件下给出 ORA-01841 错误?
【发布时间】:2018-12-19 06:43:14
【问题描述】:
SQL> select to_date('    ','YYYYMMDD') from dual;
ERROR at line 1:
ORA-01841: (full) year must be between -4713 and +9999, not be 0

SQL> select * from dual where to_date('    ','YYYYMMDD') = '19960512' or 1 = 2;
ERROR at line 1:
ORA-01841: (full) year must be between -4713 and +9999, not be 0

SQL> select * from dual where to_date('    ','YYYYMMDD') = '19960512' or 1 = 1;
Success.

在前两个语句中,我们将空格传递给 TO_DATE 函数,这会给出预期的错误。 但是

现在在第三个语句中,我们将空格传递给 TO_DATE oracle 函数,这是不允许的。但它仍然成功执行。

为什么?

在实际代码中,我可能会从变量中传递空格,而 OR 部分可能是也可能不是。

谁能解释一下这种行为以及如何处理以获取错误?

P.S - 我在 Oracle 12g EE 上

【问题讨论】:

    标签: sql oracle to-date


    【解决方案1】:

    Oracle 在这里使用短路逻辑。由于已知1=1 为真,Oracle 优化了对其他条件的评估。由于它从未被评估过,因此您将无效参数传递给它的事实是无关紧要的。

    【讨论】:

    • 赞成,但最好能看到您的答案的文档参考,因为 SQL 中的 AFAIK 通常没有保证短路顺序(与 Java 和 C# 等应用程序语言不同, 确实有这样的保证规则)。
    • Justin Cave here stackoverflow.com/questions/8900631/… 断言优化器以任何顺序查找真相,以便它可以缩短一组谓词,但不参考 oracle 文档
    • 为了 OR 条件,我添加了 1=1 ,但这部分取决于流量,可能是也可能不是。但是,当空格传递给 TO_DATE 时,我如何确保收到错误?
    • 添加1=1 影响了结果。如果像这样添加额外的条件:select * from dual where to_date(' ','YYYYMMDD') = '19960512' or to_date('19960512','YYYYMMDD') = '19960512',那么您将得到您预期的错误。所以解决方案:如何处理出现错误?是不要使用1=1
    【解决方案2】:

    另一个答案没有处理您仍然希望得到错误的问题部分

    我建议您单独进行日期解析。如果这是一个存储过程,不要像你在这里所做的那样放置查询,创建一个日期变量,使用 to_date 为其分配一个日期值(如果用户给出空格,它将崩溃)然后使用日期变量在查询中。这样,如果提供了错误数据,oracle 仍然有机会扼杀它。

    或者让您的存储过程采用日期类型参数而不是字符串,以强制调用程序提供合理且一致的日期

    【讨论】:

    • 这些带有 or 条件的查询位于数据层中,并且从数据库中获取字符串并在查询中传递给 TO_DATE。
    • 我有点不明白你的意思,但如果你觉得除了这样的查询别无他法,那么你将不得不做其他事情来强制崩溃;选择 to_date 并将其放在 where 子句中,或者将其放在强制排序的情况下。我仍然认为,如果您说查询就是全部,那么您应该正确地对其进行参数化。这个日期根本不应该是空格。还有一个论点是,如果您预计某种类型的不良数据应该处理而不是崩溃,以尽量减少技术支持团队必须做的工作量。
    【解决方案3】:

    您可以在or 分支内重复转换,这显然有点混乱:

    select * from dual
    where to_date('    ','YYYYMMDD') = '19960512'
    or (1 = 1 and to_date('    ','YYYYMMDD') != '19960512');
    
    Error report -
    ORA-01841: (full) year must be between -4713 and +9999, and not be 0
    

    或者,如果您唯一需要担心的无效日期场景是空格,那么您可以执行其他操作以使该分支在这种情况下评估为 false:

    select * from dual
    where to_date('    ','YYYYMMDD') = '19960512'
    or (1 = 1 and trim('    ') is not null);
    

    从数据库中获取字符串并在查询中传递给 TO_DATE

    表示您将日期作为字符串存储在数据库中,which is not a good idea;因此,如果您可以在其中有空格,则您可以拥有任何东西,并且还必须处理其他潜在错误,is not null 方法无法捕获。

    并且这些查询都不会因传入的空字符串(即 null)而出错 - 但您的前两个原始查询也不会;他们都会得到 no-data-found。


    当然,'19960512' 不是日期,所以您在这里进行额外的隐式转换;通过to_date() 传递它会更安全,或者如果它确实是一个固定值,则使用日期文字:

    where to_date('    ','YYYYMMDD') = date '1996-05-12'
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-05-03
      • 2019-06-03
      • 1970-01-01
      • 2015-05-23
      • 1970-01-01
      • 1970-01-01
      • 2021-02-10
      • 1970-01-01
      相关资源
      最近更新 更多