4.2.14. Expression Evaluation Rules

4.2.14. Expression Evaluation Rules

4.2.14.表达式执行规则

The order of evaluation of subexpressions is not defined. In particular, the inputs of an operator or function are not necessarily evaluated left-to-right or in any other fixed order.

子表达式的执行顺序并未规定。特别的,运算符或函数的输入并不是必须的从左到右执行,或者是以其他的固定顺序。

Furthermore, if the result of an expression can be determined by evaluating only some parts of it, then other subexpressions might not be evaluated at all. For instance, if one wrote:

甚者,如果表达式的结果可以仅执行部分即可确定,则剩余的子表达式可能根本就不会执行了。例如:

SELECT true OR somefunc();

then somefunc() would (probably) not be called at all. The same would be the case if one wrote:

那么somefunc()可能根本就不会被调用。以下与此相同:

SELECT somefunc() OR true;

Note that this is not the same as the left-to-right “short-circuiting” of Boolean operators that is found in some programming languages.

请注意,这与在某些编程语言中发现的布尔运算符从左到右的“执行”不同。

As a consequence, it is unwise to use functions with side effects as part of complex expressions. It is particularly dangerous to rely on side effects or evaluation order in WHERE and HAVING clauses, since those clauses are extensively reprocessed as part of developing an execution plan. Boolean expressions (AND/OR/NOT combinations) in those clauses can be reorganized in any manner allowed by the laws of Boolean algebra.

因此,将带有副作用的函数用作复杂表达式的一部分是不明智的。 在WHERE和HAVING子句中依靠副作用或评估顺序特别危险,因为这些子句在制定执行计划时会被大量重新处理。 这些子句中的布尔表达式(AND / OR / NOT组合)可以按照布尔代数定律允许的任何方式进行重组。

When it is essential to force evaluation order, a CASE construct (see Section 9.17) can be used. For example, this is an untrustworthy way of trying to avoid division by zero in a WHERE clause:

如果有必要强制规定执行顺序,则可以使用CASE(参阅9.17节)。例如, 例如,这是一种试图避免在WHERE子句中被零除的不可靠方法:

SELECT ... WHERE x > 0 AND y/x > 1.5;

But this is safe:

以下为安全的示例:

SELECT ... WHERE CASE WHEN x > 0 THEN y/x > 1.5 ELSE false END;

A CASE construct used in this fashion will defeat optimization attempts, so it should only be done when necessary. (In this particular example, it would be better to sidestep the problem by writing y > 1.5*x instead.)

此处使用的CASE会对优化尝试有硬性,所以仅应在必要时才作此调整。(在此示例中,将条件改为y>1.5*x会更好一些。)

CASE is not a cure-all for such issues, however. One limitation of the technique illustrated above is that it does not prevent early evaluation of constant subexpressions. As described in Section 38.7, functions and operators marked IMMUTABLE can be evaluated when the query is planned rather than when it is executed. Thus for example

然而,CASE并不是此类事项的万能药。 上例中说明的技术的一个局限性在于它不能阻止对常量子表达式的早期评估。 如第38.7节所述,标记为IMMUTABLE的函数和运算符可以在计划查询时而不是在执行查询时进行评估。 因此,例如

SELECT CASE WHEN x > 0 THEN x ELSE 1/0 END FROM tab;

is likely to result in a division-by-zero failure due to the planner trying to simplify the constant subexpression,even if every row in the table has x > 0 so that the ELSE arm would never be entered at run time.

会因为执行计划尝试简化常量子表达式而导致0作为除数的报错,即使表中的每一行均大于0,ELSE中的语句在实际执行中并不会执行。

While that particular example might seem silly, related cases that don't obviously involve constants can occur in queries executed within functions, since the values of function arguments and local variables can be inserted into queries as constants for planning purposes. Within PL/pgSQL functions, for example, using an IF-THEN-ELSE statement to protect a risky computation is much safer than just nesting it in a CASE expression.

尽管该特定示例可能看起来傻傻的,但是在函数内执行的查询中可能会出现明显不涉及常量的相关情况,因为出于规划目的,可以将函数参数和局部变量的值作为常量插入查询中。 例如,在PL/pgSQL函数中,使用IF-THEN-ELSE语句来保护有风险的计算比仅将其嵌套在CASE表达式中要安全得多。

Another limitation of the same kind is that a CASE cannot prevent evaluation of an aggregate expression contained within it, because aggregate expressions are computed before other expressions in a SELECT list or HAVING clause are considered. For example, the following query can cause a division-by-zero error despite seemingly having protected against it:

同类的另一个限制是,CASE无法阻止其中包含的聚合表达式的求值,因为聚合表达式是在考虑SELECT列表或HAVING子句中的其他表达式之前计算的。 例如,尽管看似已对其进行了保护,但以下查询仍可能导致被零除的错误:

SELECT CASE WHEN min(employees) > 0

THEN avg(expenses / employees)

END

FROM departments;

The min() and avg() aggregates are computed concurrently over all the input rows, so if any row has employees equal to zero, the division-by-zero error will occur before there is any opportunity to test the result of min(). Instead, use a WHERE or FILTER clause to prevent problematic input rows from reaching an aggregate function in the first place.

min()和avg()聚合函数是同时计算的,所以如果有employees为0的时候,则会在没有度imin()进行测试之前就会导致被零除的报错。可以使用WHERE或者FILTER子句以确保输入到聚合函数的行的正确性。

测试:

发布了341 篇原创文章 · 获赞 53 · 访问量 88万+

猜你喜欢

转载自blog.csdn.net/ghostliming/article/details/104307015