考虑以下查询并注意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使用与其在和/或逻辑中使用的相同(或类似)的短路。例如,如果EMP.STATUS = 2,它将不会打扰评估表达式的其余部分,因为整个表达式无论如何都会返回false。
在我的例子中,正在为db中的每个雇员调用CALCULATE_INCENTIVE函数,而不是仅调用前三个WHERE表达式返回的9个记录。我甚至尝试将括号括在我想要组合在一起的特定表达式进行短路评估,但我无法弄明白。
如果任何以前的表达式返回false,那么任何人都有任何想法如何评估CALCULATE_INCENTIVE 而不是?
答案 0 :(得分: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;
答案 1 :(得分: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)
上创建了基于函数的索引,优化器可能会使用它而不是在运行时调用该函数。但是,如果它确定更高效,那么优化器可以完全自由地决定在任何一种情况下为每一行调用该函数。