Oracle将天到秒的间隔比较为整数

时间:2018-10-25 13:34:06

标签: oracle plsql types oracle12c intervals

当我想用整数比较天到秒的间隔时,我想知道在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评估为真吗?

2 个答案:

答案 0 :(得分:2)

您看到的似乎是一个错误,或者至少是未记录的行为。因此,不幸的是,我认为您需要向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转换为间隔类型,也不是将间隔转换为数字,或者将计算中的间隔或时间戳转换为日期...

似乎没有任何数值使比较返回false-不管是左侧的计算(可能是被重写为第二版的计算)还是固定间隔变量。这使它看起来更像是一个漏洞,而不是故意的。

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

-- Correct way
IF v_date1 < v_date2 + 2 THEN

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

答案 1 :(得分:1)

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

您应该将间隔视为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