今天我遇到了一个我无法解释的情况,我希望你能。
归根结底是这样的:
假设function sleep() return number;循环10秒,然后返回1。
类似的sql查询
SELECT systimestamp s1, sleep(), systimestamp s2 from dual;将为s1和s2生成两个相同的值。所以这是某种程度的优化。
PLSQL-块
a_timestamp := systimestamp + 5sec;
IF systimestamp < a_timestamp and sleep() = 1 and systimestamp > a_timestamp THEN
[...]
END IF;将计算为true,因为表达式从左到右得到求值,而且由于睡眠(),第二个systimestamp比第一个大10秒,a_timestamp介于两者之间。+5秒的语法是伪代码,但是请原谅我。因此,在这里,systimestamp没有被优化。
但现在它变得古怪了:
IF systimestamp between systimestamp and systimestamp THEN [...]总是假的,但是
IF systimestamp + 0 between systimestamp + 0 and systimestamp + 0 THEN [...]永远都是真的。
为什么?我很困惑..。
这种情况也发生在sysdate上。
发布于 2020-06-18 11:57:47
当你这样做时:
if systimestamp between systimestamp and systimestamp then对不确定的systimestamp调用只进行三次评估,每次得到的结果略有不同。
你可以看到同样的效果
if systimestamp >= systimestamp then它也总是返回false。
不过,也不总是这样。如果服务器足够快,并且/或它所在的平台的精度很低--足够精确到时间戳小数秒(例如,在Windows上,我认为它仍然将精度限制在毫秒),那么所有这些调用在某些时候或大部分时间仍然可以得到相同的值。
SQL中的情况有些不同;作为一个等价的:
select *
from dual
where systimestamp between systimestamp and systimestamp;将始终返回一行,因此条件始终为真。那是文件中明确提到
所有返回当前系统日期时间信息的datetime函数,例如SYSDATE、SYSTIMESTAMP、CURRENT_TIMESTAMP等,都会为每个SQL语句计算一次,而不管它们在该语句中引用了多少次。
PL/SQL中没有这样的限制/优化(取决于您如何看待它)。当您使用between 上面确实写着时
A和b之间表达式x的值被定义为与表达式(x>=a)和(x<=b)的值相同。表达式x将只计算一次。
和SQL引用还提到。
在SQL中,可能会对expr1进行多次评估。如果中间表达式出现在PL/SQL中,则保证只计算expr1一次。
但是它并没有提到跳过a和b(或expr2或expr3)的计算,甚至对于PL/SQL中的系统日期时间函数也是如此。
因此,您的between中的所有三个表达式都将被计算,它将对systimestamp进行三个单独的调用,它们(通常)都会得到稍微不同的结果。你实际上得到的结果是:
if initial_time between initial_time + 1 microsecond and initial_time + 2 microseconds then或者换个说法
if (initial_time >= initial_time + 1 microsecond) and (initial_time <= initial_time + 2 microseconds) then虽然(initial_time <= initial_time + 2 microseconds)总是正确的,但是(initial_time >= initial_time + 1 microsecond)必须是假的--除非对于平台/服务器/调用,第一个和第三个评估之间的间隔实际上是零。当条件为零时,计算结果为true;其余时间,当存在任何可测量的延迟时,它将计算为false。
其他示例都以删除小数秒的方式操作时间戳,方法是将部分或全部结果转换为日期,如@Connor所示(我在注释中提到了这一点)。这与你的核心问题(为什么if systimestamp between systimestamp and systimestamp then (通常)是假的)并没有什么关系。
发布于 2020-06-18 03:58:02
如果将数字添加到时间戳中,则不会得到timestamp...you获取日期。因此,您已经去掉了小数秒部分,因此看起来它们应该相等的比较可能不是。
SQL> select systimestamp, systimestamp+0 from dual;
SYSTIMESTAMP SYSTIMESTAMP+0
--------------------------------------------------------------------------- -------------------
18-JUN-20 11.57.28.350000 AM +08:00 18/06/2020 11:57:28
SQL> select * from dual where systimestamp > sysdate;
D
-
X如果要将天数等添加到时间戳中,请使用间隔数据类型而不是数字。
https://stackoverflow.com/questions/62437737
复制相似问题