Oracle 将天与秒的时间间隔与整数进行比较

Oracle comparing day to second interval to an integer

我想知道当我尝试将天数与秒数间隔与整数进行比较时,oracle 背后发生了什么。

示例如下。

SET SERVEROUTPUT ON;
DECLARE
    v_date1 TIMESTAMP := current_timestamp ;
    v_date2 TIMESTAMP := current_timestamp - 150;
BEGIN
    -- Wrong way. But what is happening here?
    IF v_date1 - v_date2 < 2 THEN
        DBMS_OUTPUT.PUT_LINE('YES - why?');
    ELSE
        DBMS_OUTPUT.PUT_LINE('NO');
    END IF;
    -- Correct way
    IF v_date1 < v_date2 + 2 THEN
        DBMS_OUTPUT.PUT_LINE('YES');
    ELSE
        DBMS_OUTPUT.PUT_LINE('NO - works as expected');
    END IF;
    -- Another correct way
    IF v_date1 - v_date2 < INTERVAL '2' DAY THEN
        DBMS_OUTPUT.PUT_LINE('YES');
    ELSE
        DBMS_OUTPUT.PUT_LINE('NO - works as expected as well');
    END IF;
END;

有人可以向我解释为什么第一个 IF 的计算结果为真吗?

您看到的似乎是错误,或者至少是未记录的行为。所以很遗憾,我认为您需要向 Oracle 提出服务请求才能获得解释。只有他们可以窥视引擎盖下,看看它是否在做他们期望的事情,或者是他们没有想到的事情。


分解一些步骤,不仅仅是针对您的具体问题:

v_date1 TIMESTAMP := current_timestamp ;

强制转换 current_timestamp,这是您的会话时区中的时间,转换为普通时间戳 - 丢失时区信息。

v_date2 TIMESTAMP := current_timestamp - 150;

有两个步骤; current_timestamp - 150 给你一个日期,丢失小数秒 时区信息,然后将其转换回时间戳。最好使用 - interval '150' day,但我知道您只是在此处设置一些虚拟数据。

IF v_date1 - v_date2 < 2 THEN

也是多个阶段:v_date1 - v_date2 给你一个间隔,例如+150 00:00:00.975444。 (请注意,这并不正好是 150 天,部分原因是两次 current_dtimestamp 调用之间经过的时间很短,但比你预期的差值要大得多——因为早期反弹的精度损失通过日期数据类型。)

那么比较本身就是有趣的地方。看起来正在进行隐式数据转换,但您正在尝试将间隔与数字进行比较,并且没有隐式转换允许这样做;并在普通 SQL 尝试做同样的比较错误:

select case when current_timestamp - (current_timestamp - 150) < 2 then 'yes' else 'no' end from dual;

ORA-00932: inconsistent datatypes: expected INTERVAL DAY TO SECOND got NUMBER

所以看起来 PL/SQL 正在做一些其他的隐式转换,但不清楚是什么,而且似乎没有在 MoS 上记录或提及。它似乎不是字符串比较,也不是原始的,也不是将 2 转换为任一间隔类型,也不是将间隔转换为数字,也不是间隔或时间戳在日期计算中...

似乎没有任何数值使该比较不成立 return - 左侧是否是一个计算(它可能被重写为更像您的第二个版本的东西)或固定区间变量。这让它看起来更像是一个错误而不是故意的。

原始代码中的最后一件事:

-- Correct way
IF v_date1 < v_date2 + 2 THEN

这也不是很正确,尽管它可能看起来很挑剔; v_date + 2 给你一个日期而不是时间戳,所以如果你之前没有丢失小数秒精度(使用 - 150)那么你会在这一点上。现在您将时间戳与日期进行比较,这涉及更多的隐式转换。您可以将数字 2 更改为 2 天间隔,但这在逻辑上与您最终(正确)的比较相同。

我想这不是错误,而是错误的源代码。

您应该将间隔视为 INTERVAL

DECLARE
    v_date1 TIMESTAMP := CURRENT_TIMESTAMP ;
    v_date2 TIMESTAMP := v_date1 - INTERVAL '150' DAY;
BEGIN
    DBMS_OUTPUT.PUT_LINE(v_date1 - v_date2);
    -- Right way.
    IF v_date1 - v_date2 < INTERVAL '2' DAY THEN
        DBMS_OUTPUT.PUT_LINE('YES - why?');
    ELSE
        DBMS_OUTPUT.PUT_LINE('NO');
    END IF;
    -- Right way
    IF v_date1 < v_date2 + INTERVAL '2' DAY THEN
        DBMS_OUTPUT.PUT_LINE('YES');
    ELSE
        DBMS_OUTPUT.PUT_LINE('NO - works as expected');
    END IF;
    -- Right correct way
    IF v_date1 - v_date2 < INTERVAL '2' DAY THEN
        DBMS_OUTPUT.PUT_LINE('YES');
    ELSE
        DBMS_OUTPUT.PUT_LINE('NO - works as expected as well');
    END IF;
END;

+000000150 00:00:00.000000000
NO
NO - works as expected
NO - works as expected as well