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