Oracle SQL:是否存在 Oracle 可以解释的最大日期差异

Oracle SQL: Is There a Maximum Date Difference Oracle Can Interpret

我正在研究 sql,它在 table 中查找行,其中行 'last_run' 日期 + 'frequency'(以分钟为单位)大于当前 date/time。我注意到 Oracle 可以理解的日期比较似乎有一个上限。

例如这个查询;

with tests as 
    (
    select
        'TEST 1'  as code,
        99999999 as frequency,
        sysdate as last_run
    from dual
        union
    select
        'TEST 2'  as code,
        99999999999 as frequency,
        sysdate as last_run
    from dual
    )
select
    p.*,
    (p.last_run + p.frequency / 24 / 60 ) as next_run
from tests p
where (p.last_run + p.frequency / 24 / 60 < sysdate or p.last_run is null)

我希望此查询 return 为空,但它 returns;

CODE FREQUENCY LAST_RUN NEXT_RUN
TEST 2 99999999999 05-OCT-2021 10:15:46 AM 15-APR-4455 08:54:46 PM

我可以通过设置 frequency = null 来解决这个问题,我的其他代码会识别出不需要考虑该行,但令我感到奇怪的是 Oracle 无法识别 4455 > 2021 年。

Oracle 中是否有一些我不知道的最大可能日期?

我是 运行 Oracle SQL 开发人员版本 18.2.0.183Oracle 数据库 19c 企业版 19.0 版。 0.0.0 - 生产.

Oracle 中有一个最大日期,YYYY-MM-DD HH24:MI:SS 格式是9999-12-31 23:59:59。这是 Oracle 文档的屏幕截图: 这是 Oracle 文档,其中讨论了有效日期值 (LINK)

问题是您在第二个查询中添加了 ~190,258 年。可能多次溢出缓冲区。碰巧你最终回到了你所做的价值。

it seems strange to me that Oracle can't recognize that the year 4455 > 2021

可以。问题是你的年份不是 4455;它是-4455。 See this db<>fiddle,使用默认的 DD-MON-RR 格式显示结果(在不同的时区)、您的输出格式和包含年号的 ISO 格式(S 格式元素)。

CODE FREQUENCY LAST_RUN NEXT_RUN
TEST 2 99999999999 2021-10-05 17:16:21 -4454-03-12 03:55:21

根据您的频率 99999999999,您要添加到当前日期的值为 69444444 天,也就是(非常粗略地)190128 年 - 显然这将使您远远超过最大日期 9999-12-31;并且确实有不同的值,如 9999999999(少一位),即 6944444 天或大约 19012 年,你会得到一个错误 - 也显示在 db<>fiddle.

问题似乎是 Oracle 在进行计算时如何操纵它的 internal representation;在添加这个大值时,年份 - 存储在两个字节中 - 正在溢出和回绕。

使用type-13版本,190128+2021 = 192149,也就是(256 * 750) + 149。750放不下一个字节,所以你得到模数,也就是238。这样就可以了计算日期的前两个字节为 149,238。这实际上对应于 -4459 年:

select dump(date '-4459-01-01') from dual;

Typ=13 Len=8: 149,238,1,1,0,0,0,0

这足以证明这就是正在发生的事情 - 假设计算超出预期范围并且它可能试图在某处进行无效的闰日计算。不过,关键是生成的包装值代表该内部符号中的有效年份。

取较小的值,19012+2021 = 20133,即(256 * 82) + 41。现在没有换行,所以计算出的日期的前两个字节为41,82。现在 不是 有效年份,因此 Oracle 知道抛出 ORA-01841 异常。

因此,您需要将频率值限制为永远不会超过 9999-12-31 的数字,或者在 运行 时间对 9999-12-31 减去当前日期进行测试- 如果它太大,请忽略它。那就是如果你想要一个看似神奇的数字。