2

考虑以下查询并注意 CALCULATE_INCENTIVE 函数:

SELECT EMP.* FROM EMPLOYEES EMPS
WHERE
    EMP.STATUS = 1 AND
    EMP.HIRE_DATE > TO_DATE('1/1/2010') AND
    EMP.FIRST_NAME = 'JOHN' AND
    CALCULATE_INCENTIVE(EMP.ID) > 1000
ORDER BY EMPS.ID DESC;

我的印象是 Oracle 使用了与 .NET 在其和/或逻辑中使用的相同(或相似)短路。例如,如果 EMP.STATUS = 2,它不会计算表达式的其余部分,因为整个表达式无论如何都会返回 false。

在我的例子中,CALCULATE_INCENTIVE 函数在数据库中的每个员工上被调用,而不是仅仅在前三个 WHERE 表达式返回的 9 条记录上被调用。我什至尝试在我想组合在一起进行短路评估的特定表达式周围加上括号,但我无法弄清楚。

如果前面的任何表达式返回 false,任何人都知道如何让 CALCULATE_INCENTIVE不被评估?

4

2 回答 2

2

一种方法是将主要标准放入 Oracle 无法优化的子查询中,然后将次要标准放入外部查询中。确保 Oracle 不会优化子查询的最简单方法是在 select 语句中包含 rownum:

SELECT * FROM (
   SELECT EMP.*, ROWNUM 
   FROM EMPLOYEES EMPS
   WHERE
       EMP.STATUS = 1
       AND EMP.HIRE_DATE > TO_DATE('1/1/2010')
       AND EMP.FIRST_NAME = 'JOHN')
WHERE CALCULATE_INCENTIVE(ID) > 1000
ORDER BY EMPS.ID DESC;
于 2011-06-17T12:44:09.197 回答
1

Oracle 支持 PL/SQL 中的短路评估。然而,在 SQL 中,优化器可以自由地以它想要的任何顺序评估谓词,将谓词推送到视图和子查询中,并以其他方式转换它认为合适的 SQL 语句。这意味着您不应依赖以特定顺序应用的谓词,并使出现在 WHERE 子句中的顺序谓词本质上是不相关的。可用的索引、存在的优化器统计信息、优化器参数和系统统计信息都比 WHERE 子句中谓词的顺序重要得多。

例如,在 PL/SQL 中,您可以使用一个在实际调用时抛出错误的函数来证明这一点。

SQL> ed
Wrote file afiedt.buf

  1  create function throw_error( p_parameter IN NUMBER )
  2    return number
  3  as
  4  begin
  5    raise_application_error( -20001, 'The function was called' );
  6    return 1;
  7* end;
SQL> /

Function created.

SQL> ed
Wrote file afiedt.buf

  1  declare
  2    l_num NUMBER;
  3  begin
  4    l_num := 1;
  5    if( l_num = 2 and throw_error( l_num ) = 2 )
  6    then
  7      null;
  8    else
  9      dbms_output.put_line( 'Short-circuited the AND' );
 10    end if;
 11    if( l_num = 1 or throw_error( l_num ) = 2 )
 12    then
 13      dbms_output.put_line( 'Short-circuited the OR' );
 14    end if;
 15* end;
 16  /
Short-circuited the AND
Short-circuited the OR

PL/SQL procedure successfully completed.

另一方面,在 SQL 中,操作的顺序由优化器决定,而不是由您决定,因此优化器可以随意短路或不短路。Jonathan Gennick 有一篇很棒的文章Subquery Madness!详细讨论了这一点。在您的特定情况下,如果您在 (FIRST_NAME, HIRE_DATE, STATUS) 上有一个复合索引以及适当的统计信息,优化器几乎肯定会使用该索引来评估前三个条件,然后只调用CALCULATE_INCENTIVE满足 ID 的函数其他三个标准。如果您在CALCULATE_INCENTIVE(id),优化器可能会使用它而不是在运行时调用该函数。但是,如果优化器认为这样做会更有效,那么它可以完全自由地决定在任何一种情况下为每一行调用函数。

于 2011-06-17T14:57:12.043 回答