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
我想知道当我尝试将天数与秒数间隔与整数进行比较时,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