【问题标题】:pl/sql function called how many times?pl/sql 函数调用了多少次?
【发布时间】:2011-10-10 05:17:54
【问题描述】:

假设您有以下更新:

Update table set col1 = func(col2)
where col1<>func(col2)

func 函数每行计算两次,还是每行计算一次?

谢谢,

【问题讨论】:

标签: sql oracle plsql oracle9i


【解决方案1】:

在这种情况下,一些实验很有用(这是在 10g 上进行的)。使用下面的查询,我们可以知道使用相同参数(在这种情况下,没有)的普通函数将在每次调用时执行:

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 部分。如果我们在selectwhere 子句中使用相同的确定性函数会怎样。我们可以使用以下查询进行测试:

SELECT rand_det
FROM   all_tables
WHERE  rand_det > .5;

您可能需要运行几次才能看到我们的证明,但最终您会看到小于 0.5 的值列表。这为我们提供了即使确定性函数也被执行了两次的证据:对于它出现的每个部分执行一次。作为替代方案,您可以如下修改我们的确定性函数,然后运行后续查询,这将显示写入 @ 的 2 行987654329@.

CREATE OR REPLACE FUNCTION rand_det
   RETURN NUMBER
   DETERMINISTIC AS
BEGIN
   DBMS_OUTPUT.put_line ('Called!');
   RETURN DBMS_RANDOM.VALUE ();
END;

SELECT rand_det
FROM   all_tables;

【讨论】:

  • +1 表示实验证据。这是什么版本的预言机?
【解决方案2】:

虽然我喜欢 Allan 的回答,他展示了如何对此进行调查,但我认为真正要吸取的教训是,如果可以避免的话,你不应该依赖这个问题的答案。

Here's a useful post 来自 Tom Kyte 的主题(“我已经写了数千次了,你无法依赖 SQL 调用你的函数的次数、时间或时间。”)甚至在 11g 引入结果缓存之前,无法保证在处理 SQL 语句时函数将被调用多少次。它可能取决于执行计划,该计划会随着时间而改变。

如果您关心的是性能并且您的函数是确定性的,那么 11g 结果缓存可能就足够了——它不能保证对函数的调用次数有特定限制,但应该会显着减少冗余调用的次数。 (请参阅@cularis 答案中提供的链接。)

如果出于某种原因您确实想确保对函数的两次调用是不同的,我认为您可以强制执行此操作的唯一方法是将第二个参数添加到实际上被忽略但用于防止结果缓存或优化器将调用视为相同。

【讨论】:

    【解决方案3】:

    对于 11g,每次新出现的 col2 都会调用一次。对于下一次调用,如果启用了caching,则使用缓存的结果。

    【讨论】:

    • 哇,这很酷,超出了我的预期。但是在 9.2 中?每行调用一次还是两次?
    • 这可能是错误的。如果普遍正确,则此查询将为所有行返回相同的值:select dbms_random.value() from all_tables
    • 我怀疑缓存被禁用了,随机值也是用种子作为输入生成的。所以每次输入都不一样。
    • @cularis:使用的value 函数没有参数,因此Oracle 不知道其中涉及种子。数据库引擎不会尝试解析函数背后的代码来确定是否有来自外部来源的值,它只会查看规范中可用的信息。归根结底,这不应该归结为我们谁认为他们知道得更多:如果您能证明 Oracle 的行为符合您的建议,那么您应该这样做。
    • 我不会依赖结果缓存作为保证在 SQL 处理期间每个输入值最多调用一次函数。缓存未命中发生。此外,值得强调的是(尽管在提供的链接中提到)必须为每个功能单独启用此缓存。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多