有人可以帮我找出 Oracle (10g) AND/OR 短路吗?
考虑以下查询并注意 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 not 被评估吗?
Consider the following query and notice the CALCULATE_INCENTIVE function:
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;
I was under the impression that Oracle uses the same (or similar) short-circuitry that .NET uses in its and/or logic. For example, if EMP.STATUS = 2, it won't bother evaluating the rest of the expression since the entire expression would return false anyway.
In my case, the CALCULATE_INCENTIVE function is being called on every employee in the db rather than only on the 9 records that the first three WHERE expressions return. I've even tried putting parenthesis around the specific expressions that I want to group together for short-circuit evaluation, but I can't figure it out.
Anyone have any ideas how to get the CALCULATE_INCENTIVE not to be evaluated if any of the previous expressions return false?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
一种方法是将主要条件放入 Oracle 无法优化的子查询中,然后将次要条件放入外部查询中。确保 Oracle 不会优化子查询的最简单方法是在 select 语句中包含 rownum:
One way is to put the primary criteria into a subquery that Oracle can't optimize away, then put the secondary criteria into the outer query. The easiest way to ensure that Oracle doesn't optimize out the subquery is to include rownum in the select statement:
Oracle 支持 PL/SQL 中的短路评估。然而,在 SQL 中,优化器可以自由地按其所需的任何顺序评估谓词、将谓词推送到视图和子查询中,以及按照它认为合适的方式转换 SQL 语句。这意味着您不应依赖于按特定顺序应用的谓词,并使 WHERE 子句中出现的顺序谓词基本上不相关。可用的索引、存在的优化器统计信息、优化器参数和系统统计信息都比 WHERE 子句中谓词的顺序重要得多。
例如,在 PL/SQL 中,您可以使用一个在实际调用时抛出错误的函数来演示这一点。
另一方面,在 SQL 中,操作顺序由优化器决定,而不是由您决定,因此优化器可以随意短路或不短路。 Jonathan Gennick 有一篇很棒的文章 子查询疯狂! 详细讨论了这一点。在您的特定情况下,如果您在 (FIRST_NAME、HIRE_DATE、STATUS) 上有一个复合索引以及适当的统计信息,则优化器几乎肯定会使用该索引来评估前三个条件,然后仅调用
CALCULATE_INCENTIVE
满足其他三个条件的 ID 的函数。如果您在 CALCULATE_INCENTIVE(id) 上创建了基于函数的索引,优化器可能会使用该索引,而不是在运行时调用该函数。但是,如果优化器认为这样做会更有效,那么它可以完全自由地决定在任何一种情况下为每一行调用该函数。Oracle supports short-circuit evaluation in PL/SQL. In SQL, however, the optimizer is free to evaluate the predicates in whatever order it desires, to push predicates into views and subqueries, and to otherwise transform the SQL statement as it sees fit. This means that you should not rely on predicates being applied in a particular order and makes the order predicates appear in the WHERE clause essentially irrelevant. The indexes that are available, the optimizer statistics that are present, the optimizer parameters, and system statistics are all vastly more important than the order of predicates in the WHERE clause.
In PL/SQL, for example, you can demonstrate this with a function that throws an error if it's actually called.
In SQL, on the other hand, the order of operations is determined by the optimizer, not by you, so the optimizer is free to short-circuit or not short-circuit however it wants. Jonathan Gennick has a great article Subquery Madness! that discusses this in some detail. In your particular case, if you had a composite index on (FIRST_NAME, HIRE_DATE, STATUS) along with appropriate statistics, the optimizer would almost certainly use the index to evaluate the first three conditions and then only call the
CALCULATE_INCENTIVE
function for the ID's that met the other three criteria. If you created a function-based index onCALCULATE_INCENTIVE(id)
, the optimizer would likely use that rather than calling the function at all at runtime. But the optimizer would be perfectly free to decide to call the function for every row in either case if it decided that it would be more efficient to do so.