选择 打开 改范围 完整检索页

pgsql.cc 提供对 postgresql.org 官网内容的中文翻译,由 Pigsty 团队维护。

受支持版本: 当前版本 (18) / 17 / 16 / 15 / 14
测试与开发版本: 19 / devel
不受支持的版本: 13 / 12 / 11 / 10 / 9.6 / 9.5 / 9.4 / 9.3 / 9.2 / 9.1 / 9.0
历史版本PostgreSQL 9.2 已于 2017 年 11 月结束社区维护,本页译文保留供仍在使用旧版本的读者参考。新系统请看当前版本

4.2. 值表达式 #

值表达式被用于各种各样的环境中,例如在SELECT命令的目标列表中,作为INSERTUPDATE中的新列值,或者作为若干命令中的搜索条件。为了与表表达式(其结果是一张表)区分开来,值表达式的结果有时被称为标量。因此,值表达式也称为标量表达式(甚至简称为表达式)。表达式语法允许使用算术、逻辑、集合以及其他操作,从基本部分计算出值。

一个值表达式是下列之一:

  • 一个常量或字面值

  • 一个列引用

  • 在一个函数定义体或预备语句中的一个位置参数引用

  • 一个下标表达式

  • 一个字段选择表达式

  • 一个操作符调用

  • 一个函数调用

  • 一个聚合表达式

  • 一个窗口函数调用

  • 一个类型转换

  • 一个排序规则表达式

  • 一个标量子查询

  • 一个数组构造器

  • 一个行构造器

  • 另一个圆括号中的值表达式(用于对子表达式分组并覆盖优先级

在这个列表之外,还有一些结构可以被分类为一个表达式,但是它们不遵循任何一般语法规则。这些通常具有一个函数或操作符的语义并且在第 9 章中的合适位置解释。一个示例是IS NULL子句。

我们已经在第 4.1.2 节中讨论过常量。下面的小节会讨论剩下的选项。

4.2.1. 列引用 #

一个列可以以下面的形式被引用:

correlation.columnname

correlation 是一个表的名字(可能带有模式名限定),或者是在 FROM 子句中为某个表定义的别名。如果该列名在当前查询所用的所有表中都是唯一的,则关联名称和分隔用的句点都可以省略(另见第 7 章)。

4.2.2. 位置参数 #

位置参数引用用来表示一个由 SQL 语句外部提供的值。参数可用于 SQL 函数定义和预备语句中。某些客户端库还支持把数据值与 SQL 命令字符串分开指定,在这种情况下,参数就用来引用这些命令字符串之外的数据值。参数引用的形式是:

$number

例如,考虑一个函数dept的定义:

CREATE FUNCTION dept(text) RETURNS dept
    AS $$ SELECT * FROM dept WHERE name = $1 $$
    LANGUAGE SQL;

这里$1引用函数被调用时第一个函数参数的值。

4.2.3. 下标 #

如果一个表达式得到了一个数组类型的值,那么可以抽取出该数组值的一个特定元素:

expression[subscript]

或者抽取出多个相邻元素(一个数组切片):

expression[lower_subscript:upper_subscript]

(这里,方括号[ ]表示其字面意思)。每一个下标自身是一个表达式,它必须得到一个整数值。

通常,数组表达式必须加上圆括号,但如果要加下标的表达式只是列引用或位置参数,则圆括号可以省略。另外,当原始数组是多维数组时,多个下标可以连写。例如:

mytable.arraycolumn[4]
mytable.two_d_column[17][34]
$1[10:42]
(arrayfunction(a,b))[42]

最后一个示例中的圆括号是必需的。详见第 8.14 节

4.2.4. 字段选择 #

如果一个表达式得到一个复合类型(行类型)的值,那么可以抽取该行的指定字段:

expression.fieldname

通常,行表达式必须加上圆括号,但如果要取字段的表达式只是表引用或位置参数,则圆括号可以省略。例如:

mytable.mycolumn
$1.somecolumn
(rowfunction(a,b)).col3

(因此,限定列引用实际上只是字段选择语法的一种特例。)一种重要的特例是从复合类型的表列中抽取字段:

(compositecol).somefield
(mytable.compositecol).somefield

这里需要圆括号来显示compositecol是一个列名而不是一个表名,在第二种情况中则是显示mytable是一个表名而不是一个模式名。

你可以通过书写.*来请求一个组合值的所有字段:

(compositecol).*

这种记法的行为根据上下文会有不同,详见第 8.15.5 节

4.2.5. 操作符调用 #

对于一次操作符调用,有三种可能的语法:

expression operator expression(二元中缀操作符)
operator expression(一元前缀操作符)
expression operator(一元后缀操作符)

其中 operator 词元遵循 第 4.1.3 节 的语法规则,或者是关键词 ANDORNOT 之一,或者是一个如下形式的模式限定操作符名:

OPERATOR(schema.operatorname)

具体存在哪些操作符以及它们是一元还是二元,取决于系统或用户定义了哪些操作符。第 9 章描述了内置操作符。

4.2.6. 函数调用 #

函数调用的语法是:函数名(可能带有模式名限定)后跟一组放在圆括号内的参数列表:

function_name ([expression [, expression ... ]] )

例如,下面会计算 2 的平方根:

sqrt(2)

内置函数的列表在第 9 章中。其他函数可以由用户增加。

参数也可以选择附带名称。详见第 4.3 节

注意

接受单个复合类型参数的函数,可以选择使用字段选择语法来调用;反过来,字段选择也可以写成函数风格。也就是说,col(table)table.col 这两种记法可以互换。这种行为不是 SQL 标准的一部分,但 PostgreSQL 提供了它,因为这允许使用函数来模拟计算字段。详见第 8.15.5 节

4.2.7. 聚合表达式 #

一个聚合表达式表示将聚合函数应用于查询选中的各行。聚合函数将多个输入归约为单个输出值,例如输入的和或平均值。聚合表达式的语法可以是以下形式之一:

aggregate_name (expression [ , ... ] [ order_by_clause ] )
aggregate_name (ALL expression [ , ... ] [ order_by_clause ] )
aggregate_name (DISTINCT expression [ , ... ] [ order_by_clause ] )
aggregate_name ( * )

其中,aggregate_name 是先前定义的聚合(可以带模式名限定),expression 是任何本身不包含聚合表达式或窗口函数调用的值表达式,而 order_by_clause 是可选的 ORDER BY 子句,在下文说明。

第一种形式的聚合表达式为每一个输入行调用一次聚合。第二种形式和第一种相同,因为ALL是默认选项。第三种形式为输入行中表达式的每一个可区分值(或者对于多个表达式是值的可区分集合)调用一次聚合。最后一种形式为每一个输入行调用一次聚合,因为没有特定的输入值被指定,它通常只对于count(*)聚合函数有用。

大部分聚合函数忽略空输入,这样其中一个或多个表达式得到空值的行将被丢弃。除非另有说明,对于所有内置聚合都是这样。

例如,count(*)得到输入行的总数。count(f1)得到输入行中f1为非空的数量,因为count忽略空值。而count(distinct f1)得到f1的非空可区分值的数量。

通常,输入行以未指定的顺序传递给聚合函数。在许多情况下这无关紧要;例如,min无论以什么顺序接收输入都会产生相同的结果。但是,有些聚合函数(例如 array_aggstring_agg)的结果取决于输入行的顺序。使用这类聚合时,可选的 order_by_clause 可以用来指定所需的顺序。order_by_clause的语法与查询级 ORDER BY 子句相同,详见 第 7.5 节,但其中的表达式只能是表达式,不能是输出列的名称或编号。例如:

SELECT array_agg(a ORDER BY b DESC) FROM table;

使用多参数聚合函数时,请注意 ORDER BY 子句位于所有聚合参数之后。例如,应写成:

SELECT string_agg(a, ',' ORDER BY a) FROM table;

而不是:

SELECT string_agg(a ORDER BY a, ',') FROM table;  -- incorrect

后者在语法上是有效的,但它表示调用一个带有两个 ORDER BY 排序键的单参数聚合函数(第二个排序键是常量,因此没什么用处)。

如果在 DISTINCT 之外还指定了 order_by_clause,那么所有 ORDER BY 表达式都必须与聚合的常规参数相匹配;也就是说,不能根据未包含在 DISTINCT 列表中的表达式排序。

注意

在聚合函数中同时指定 DISTINCTORDER BY 的能力是 PostgreSQL 的扩展。

预定义的聚合函数在第 9.20 节中描述。其他聚合函数可以由用户增加。

一个聚合表达式只能出现在SELECT命令的结果列表或是HAVING子句中。在其他子句(如WHERE)中禁止使用它,因为那些子句的计算在逻辑上是在聚合的结果被形成之前。

当一个聚合表达式出现在一个子查询中(见第 4.2.11 节第 9.22 节),聚合通常在该子查询的行上被计算。但是如果该聚合的参数只包含外层变量则会产生一个异常:该聚合则属于最近的那个外层,并且会在那个查询的行上被计算。该聚合表达式从整体上则是对其所出现于的子查询的一种外层引用,并且在那个子查询的任意一次计算中都作为一个常量。只出现在结果列表或HAVING子句的限制适用于该聚合所属的查询层次。

4.2.8. 窗口函数调用 #

一次窗口函数调用表示在查询选出的部分行上应用一个类似聚合的函数。与普通聚合函数调用不同,这并不意味着把选出的行分组为单个输出行 — 每一行在查询输出中仍然独立存在。不过,窗口函数能够扫描根据该窗口函数调用的分组说明(PARTITION BY 列表)属于当前行所在组的所有行。窗口函数调用的语法是下列之一:

function_name ([expression [, expression ... ]]) OVER window_name
function_name ([expression [, expression ... ]]) OVER ( window_definition )
function_name ( * ) OVER window_name
function_name ( * ) OVER ( window_definition )

其中 window_definition 的语法为:

[ existing_window_name ]
[ PARTITION BY expression [, ...] ]
[ ORDER BY expression [ ASC | DESC | USING operator ] [ NULLS { FIRST | LAST } ] [, ...] ]
[ frame_clause ]

而可选的 frame_clause 可以是以下形式之一:

{ RANGE | ROWS } frame_start
{ RANGE | ROWS } BETWEEN frame_start AND frame_end

其中 frame_startframe_end 可以是以下形式之一:

UNBOUNDED PRECEDING
value PRECEDING
CURRENT ROW
value FOLLOWING
UNBOUNDED FOLLOWING

这里,expression表示任何自身不含有窗口函数调用的值表达式。PARTITION BYORDER BY列表与整个查询的GROUP BYORDER BY子句具有本质上相同的语法和语义,唯一例外是它们的表达式始终只能是表达式,不能是输出列的名称或编号。window_name是对定义在查询的WINDOW子句中的一个命名窗口声明的引用。还可以使用在WINDOW子句中定义命名窗口的相同语法在圆括号内给定一个完整的window_definition,详见SELECT参考页。值得指出的是,OVER wname并不严格地等价于OVER (wname),后者表示复制并修改窗口定义,并且在被引用窗口声明包括一个帧子句时会被拒绝。

frame_clause指定构成窗口帧的行集合,供那些作用于帧而不是整个分区的窗口函数使用。如果frame_end被省略,则默认为CURRENT ROW。限制是frame_start不能为UNBOUNDED FOLLOWINGframe_end不能为UNBOUNDED PRECEDING,并且在上述列表中,frame_end选项不能出现在frame_start选项之前 — 例如,不允许RANGE BETWEEN CURRENT ROW AND value PRECEDING。默认的帧选项是RANGE UNBOUNDED PRECEDING,它和RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW相同;这会把该帧设置为从分区开始一直到当前行的最后一个ORDER BY同等行的所有行(如果没有ORDER BY,则是所有行)。一般来说,UNBOUNDED PRECEDING表示该帧开始于分区的第一行,类似地UNBOUNDED FOLLOWING表示该帧结束于分区的最后一行(不管是RANGE还是ROWS模式)。在ROWS模式中,CURRENT ROW表示该帧开始或结束于当前行;而在RANGE模式中,它表示该帧开始或结束于当前行在ORDER BY次序中的第一个或最后一个同等行。value PRECEDINGvalue FOLLOWING目前只允许用于ROWS模式。它们表示帧开始或结束于当前行之前或之后那么多行的位置。value必须是一个不包含任何变量、聚合函数或窗口函数的整数表达式。其值不能为空值或负数;但可以为零,这会只选择当前行本身。

内置的窗口函数在表 9.48中介绍。用户可以加入其他窗口函数。此外,任何内置的或者用户定义的聚合函数都可以被用作窗口函数。

使用*的语法被用来把参数较少的聚合函数当作窗口函数调用,例如count(*) OVER (PARTITION BY x ORDER BY y)。星号(*)通常不被用于非聚合的窗口函数。与普通聚合函数不同,聚合窗口函数不允许在函数参数列表中使用DISTINCTORDER BY

只有在SELECT列表和查询的ORDER BY子句中才允许窗口函数调用。

更多关于窗口函数的信息可以在第 3.5 节第 9.21 节第 7.2.4 节中找到。

4.2.9. 类型转换 #

一个类型转换指定从一种数据类型到另一种数据类型的转换。PostgreSQL接受两种等价的类型转换语法:

CAST ( expression AS type )
expression::type

CAST语法遵从 SQL,而用::的语法是PostgreSQL的历史用法。

当类型转换被应用于一种已知类型的值表达式时,它表示一次运行时类型转换。只有在已经定义了合适的类型转换操作时,该类型转换才会成功。注意,这与常量上的类型转换(如第 4.1.2.7 节中所示)略有不同。应用于未修饰字符串字面量的类型转换,表示为一个字面常量值赋予初始类型,因此它对任意类型都能成功(前提是该字符串字面量的内容符合该数据类型的输入语法)。

如果一个值表达式必须产生的类型没有歧义(例如当它被赋给一个表列时),通常可以省略显式类型转换,在这种情况下系统会自动应用类型转换。但是,自动类型转换只会对那些在系统目录中被标记为可以隐式应用的类型转换执行。其他类型转换必须使用显式类型转换语法来调用。施加这一限制的目的是防止出人意料的转换被悄无声息地应用。

还可以用类似函数调用的语法来指定类型转换:

typename ( expression )

不过,这只对那些名称本身也可作为函数名使用的类型有效。例如,double precision 不能用这种方式,但等效的 float8 可以。另外,由于语法冲突,名称 intervaltimetimestamp 只有在使用双引号引用时才能采用这种写法。因此,函数风格的类型转换语法会带来不一致性,通常应尽量避免。

注意

函数风格的语法事实上只是一次函数调用。当两种标准类型转换语法之一被用来做一次运行时转换时,它将在内部调用一个已注册的函数来执行该转换。简而言之,这些转换函数具有和它们的输出类型相同的名字,并且因此函数风格的语法无非是对底层转换函数的一次直接调用。显然,一个可移植的应用不应当依赖于它。详见CREATE CAST

4.2.10. 排序规则表达式 #

COLLATE 子句会覆盖表达式的排序规则。它附加在所作用的表达式之后:

expr COLLATE collation

这里 collation 可以是一个带模式名限定的标识符。COLLATE 子句比操作符绑定得更紧,必要时可以使用圆括号。

如果没有显式指定排序规则,数据库系统会从表达式所涉及的列中得到一个排序规则,如果该表达式没有涉及列,则会默认采用数据库的默认排序规则。

COLLATE 子句的两种常见用途是覆盖 ORDER BY 子句中的排序顺序,例如:

SELECT a, b, c FROM tbl WHERE ... ORDER BY a COLLATE "C";

以及覆盖具有区域敏感结果的函数或操作符调用的排序规则,例如:

SELECT * FROM tbl WHERE a > 'foo' COLLATE "C";

注意,在后一种情况中,COLLATE 子句是附加到我们希望影响的那个操作符的某个输入参数上的。把 COLLATE 子句附加到该操作符或函数调用的哪个参数上并不重要,因为操作符或函数实际采用的排序规则是综合所有参数得出的,而显式的 COLLATE 子句会覆盖其他所有参数的排序规则。(不过,若把不匹配的 COLLATE 子句附加到多个参数上,则会报错。详见第 22.2 节。)因此,这会得到与前一个示例相同的结果:

SELECT * FROM tbl WHERE a COLLATE "C" > 'foo';

但是这是一个错误:

SELECT * FROM tbl WHERE (a > 'foo') COLLATE "C";

因为它尝试把一个排序规则应用到>操作符的结果,而它的数据类型是不支持排序规则的数据类型boolean

4.2.11. 标量子查询 #

标量子查询是一个放在圆括号中的普通 SELECT 查询,它恰好返回一行一列(关于如何编写查询,见第 7 章)。该 SELECT 查询会被执行,其返回的单个值会用在外围值表达式中。把一个返回多于一行或多于一列的查询当作标量子查询来用是错误的。(但如果在某次具体执行中该子查询没有返回任何行,则不算错误;其标量结果会被视为空值。)该子查询可以引用外围查询中的变量,这些变量在该子查询的每一次求值期间都会作为常量。有关其他涉及子查询的表达式,另见第 9.22 节

例如,下列语句会寻找每个州中最大的城市人口:

SELECT name, (SELECT max(pop) FROM cities WHERE cities.state = states.name)
    FROM states;

4.2.12. 数组构造器 #

数组构造器是一个表达式,它使用其成员元素的值来构造一个数组值。简单的数组构造器由关键字 ARRAY、左方括号 [、一个用于指定数组元素值的表达式列表(以逗号分隔)以及最后的右方括号 ] 组成。例如:

SELECT ARRAY[1,2,3+4];
  array
---------
 {1,2,7}
(1 row)

默认情况下,数组元素类型是成员表达式的公共类型,其确定规则与 UNIONCASE 结构相同(见 第 10.5 节)。你可以通过把数组构造器显式转换为所需类型来覆盖这一点,例如:

SELECT ARRAY[1,2,22.7]::integer[];
  array
----------
 {1,2,23}
(1 row)

这与把每个表达式分别转换为数组元素类型具有相同的效果。关于类型转换的更多信息,见 第 4.2.9 节

多维数组值可以通过嵌套数组构造器来构造。在内部构造器中,关键字 ARRAY 可以省略。例如,下面两种写法产生相同的结果:

SELECT ARRAY[ARRAY[1,2], ARRAY[3,4]];
     array
---------------
 {{1,2},{3,4}}
(1 row)

SELECT ARRAY[[1,2],[3,4]];
     array
---------------
 {{1,2},{3,4}}
(1 row)

由于多维数组必须是长方形的,因此同一级别的内部构造器必须生成维度完全相同的子数组。施加到外层 ARRAY 构造器上的任何类型转换,都会自动传播到所有内层构造器。

多维数组构造器中的元素可以是任何能够生成适当类型数组的表达式,而不仅仅是子 ARRAY 构造。例如:

CREATE TABLE arr(f1 int[], f2 int[]);

INSERT INTO arr VALUES (ARRAY[[1,2],[3,4]], ARRAY[[5,6],[7,8]]);

SELECT ARRAY[f1, f2, '{{9,10},{11,12}}'::int[]] FROM arr;
                     array
------------------------------------------------
 {{{1,2},{3,4}},{{5,6},{7,8}},{{9,10},{11,12}}}
(1 row)

你也可以构造空数组,但由于不可能存在没有类型的数组,因此必须把空数组显式转换为所需类型。例如:

SELECT ARRAY[]::integer[];
 array
-------
 {}
(1 row)

也可以根据子查询的结果构造数组。在这种形式中,数组构造器写为关键字 ARRAY 后跟一个用圆括号(而不是方括号)括起的子查询。例如:

SELECT ARRAY(SELECT oid FROM pg_proc WHERE proname LIKE 'bytea%');
                                 array
-----------------------------------------------------------------------
 {2011,1954,1948,1952,1951,1244,1950,2005,1949,1953,2006,31,2412,2413}
(1 row)

子查询必须返回单列。生成的数组为子查询结果中的每一行包含一个元素,元素类型与子查询输出列的类型相匹配。

ARRAY 构造出来的数组值,其下标总是从 1 开始。关于数组的更多信息,见第 8.14 节

4.2.13. 行构造器 #

行构造器是一种表达式,它使用成员字段的值来构造一个行值(也称为组合值)。行构造器由关键字 ROW、左圆括号、零个或多个用于指定行各字段值的表达式(以逗号分隔),以及最后的右圆括号组成。例如:

SELECT ROW(1,2.5,'this is a test');

当列表中有多个表达式时,关键字 ROW 是可选的。

一个行构造器可以包括语法rowvalue.*,它将被扩展为该行值的元素的一个列表,就像在一个顶层SELECT列表(见第 8.15.5 节)中使用.*时发生的事情一样。例如,如果表t有列f1f2,那么这些是相同的:

SELECT ROW(t.*, 42) FROM t;
SELECT ROW(t.f1, t.f2, 42) FROM t;

注意

PostgreSQL 8.2 以前,.* 语法不会在行构造器中展开,因此写成 ROW(t.*, 42) 会创建一个具有两个字段的行,其中第一个字段是另一个行值。新的行为通常更有用。如果你需要旧版本那种嵌套行值的行为,写内层行值时不要使用 .*,例如 ROW(t, 42)

默认情况下,ROW 表达式创建的值具有匿名记录类型。如有需要,可以把它转换为具名复合类型,也就是某个表的行类型,或者使用 CREATE TYPE AS 创建的复合类型。为避免歧义,可能需要显式类型转换。例如:

CREATE TABLE mytable(f1 int, f2 float, f3 text);

CREATE FUNCTION getf1(mytable) RETURNS int AS 'SELECT $1.f1' LANGUAGE SQL;

-- No cast needed since only one getf1() exists
SELECT getf1(ROW(1,2.5,'this is a test'));
 getf1
-------
     1
(1 row)

CREATE TYPE myrowtype AS (f1 int, f2 text, f3 numeric);

CREATE FUNCTION getf1(myrowtype) RETURNS int AS 'SELECT $1.f1' LANGUAGE SQL;

-- Now we need a cast to indicate which function to call:
SELECT getf1(ROW(1,2.5,'this is a test'));
ERROR:  function getf1(record) is not unique

SELECT getf1(ROW(1,2.5,'this is a test')::mytable);
 getf1
-------
     1
(1 row)

SELECT getf1(CAST(ROW(11,'this is a test',2.5) AS myrowtype));
 getf1
-------
    11
(1 row)

行构造器可以用来构造复合值,以存储在复合类型的表列中,或者传递给接受复合参数的函数。此外,还可以比较两个行值,或者用 IS NULLIS NOT NULL 来测试一个行,例如:

SELECT ROW(1,2.5,'this is a test') = ROW(1, 3, 'not the same');

SELECT ROW(table.*) IS NULL FROM table;  -- detect all-null rows

更多详情见 第 9.23 节。行构造器也可以与子查询结合使用,详见 第 9.22 节

4.2.14. 表达式计算规则 #

子表达式的计算顺序是未定义的。特别是,操作符或函数的输入不一定会按照从左到右或任何其他固定顺序计算。

此外,如果一个表达式的结果可以通过只计算其一部分来决定,那么其他子表达式可能完全不需要被计算。例如,如果我们写:

SELECT true OR somefunc();

那么somefunc()将(可能)完全不被调用。如果我们写成下面这样也是一样:

SELECT somefunc() OR true;

注意这和一些编程语言中布尔操作符从左至右的短路不同。

因此,在复杂表达式中使用带有副作用的函数是不明智的。在WHEREHAVING子句中依赖副作用或计算顺序尤其危险,因为在建立一个执行计划时这些子句会被广泛地重新处理。这些子句中布尔表达式(AND/OR/NOT的组合)可能会以布尔代数定律所允许的任何方式被重组。

当有必要强制计算顺序时,可以使用一个CASE结构(见第 9.17 节)。例如,在一个WHERE子句中使用下面的方法尝试避免除零是不可靠的:

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

但是这是安全的:

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

一个以这种风格使用的CASE结构将使得优化尝试失败,因此只有必要时才这样做(在这个特别的示例中,最好通过写y > 1.5*x来回避这个问题)。

不过,CASE 并不是这类问题的万灵药。上述技术的一个限制是, 它无法阻止常量子表达式被提前计算。如第 35.6 节 中所述,当查询被规划而不是被执行时,被标记成 IMMUTABLE的函数和操作符可以被计算。因此

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

很可能会导致一次除零失败,因为规划器尝试简化常量子表达式。即便是 表中的每一行都有x > 0(这样运行时永远不会进入到 ELSE分支)也是这样。

虽然这个例子看起来有些可笑,但在函数内部执行的查询中,也可能出现一些看上去并未明显涉及常量的相关情况,因为函数参数值和局部变量值为了规划目的,可能会作为常量插入到查询中。例如,在 PL/pgSQL 函数中,使用 IF-THEN-ELSE 语句来保护一项有风险的计算,要比仅仅把它嵌套进一个 CASE 表达式安全得多。

另一个同类型的限制是,一个CASE无法阻止其所包含的聚合表达式 的计算,因为在考虑SELECT列表或HAVING子句中的 其他表达式之前,会先计算聚合表达式。例如,下面的查询可能会导致除零错误, 尽管看起来似乎已经对这种情况做了防护:

SELECT CASE WHEN min(employees) > 0
            THEN avg(expenses / employees)
       END
    FROM departments;

min()avg()聚合会在所有输入行上并行地计算, 因此如果任何行有employees等于零,在有机会测试 min()的结果之前,就会发生除零错误。取而代之的是,可以使用 一个WHERE子句来首先阻止有问题的输入行到达 一个聚合函数。

提交更正

译文有误、术语不当或页面显示问题,请到译文仓库 pgsty/pgdoc 报告译文问题。 英文原文本身的问题,请在当前版本的对应页面向上游反馈;上游不再修订已结束维护的版本。