假设您有以下更新:
Update table set col1 = func(col2)
where col1<>func(col2)
func 函数每行评估两次,或每行评估一次?
谢谢,
答案 0 :(得分:11)
这种情况下,某些实验很有用(这是在10g上进行的)。使用以下查询,我们可以判断每次调用时都会执行使用相同参数(在本例中为none)的正常函数:
select dbms_random.value() from all_tables
这是因为Oracle假定某个函数不会一致地返回相同的值,除非您另有说明。我们可以通过使用deterministic
关键字创建函数来实现这一点:
CREATE FUNCTION rand_det
RETURN NUMBER
DETERMINISTIC AS
BEGIN
RETURN DBMS_RANDOM.VALUE ();
END;
在第一个查询中使用此函数而不是dbms_random
告诉我们该查询只执行一次,尽管有很多调用。但这只能澄清select
部分。如果我们在select
和where
子句中使用相同的确定性函数,该怎么办?我们可以使用以下查询来测试:
SELECT rand_det
FROM all_tables
WHERE rand_det > .5;
您可能需要多次运行才能查看我们的证明,但最终,您会看到一个小于0.5的值列表。这为我们提供了证据,即使确定性函数也被执行两次:它出现的每个部分一次。作为替代,您可以如下修改我们的确定性函数,然后运行后续查询,这将显示写入{的2行{1}}。
DBMS_OUTPUT
答案 1 :(得分:10)
虽然我喜欢Allan的回答来展示如何调查此问题,但我认为真正的教训是,如果你能避免,你不应该依赖这个问题的答案。
来自Tom Kyte的主题Here's a useful post(“我已经写了数千次,你不能依赖多少次,或者SQL何时或者是否会调用你的函数。”)甚至在11g引入结果之前缓存,无法保证在处理SQL语句时调用函数的次数。它可能取决于执行计划,执行计划可能会随着时间而变化。
如果你关心的是性能并且你的功能是确定性的,11g结果缓存可能就足够了 - 它不能保证对函数调用次数的特定限制,但应该显着减少冗余调用的数量。 (参见@cularis回答中提供的链接。)
如果由于某种原因你确实想要确保对函数的两次调用是不同的,我认为你可以强制执行的唯一方法是向实际被忽略的函数添加第二个参数但是用于防止结果缓存或优化器将调用视为相同。
答案 2 :(得分:2)
对于11g,每次出现col2时都会调用一次。对于下一次调用,如果启用了caching,则使用缓存结果。