↑↓ 选择 ↵ 打开 ⌫ 改范围 完整检索页

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 / 8.4 / 8.3 / 8.2 / 8.1 / 8.0 / 7.4 / 7.3 / 7.2 / 7.1
历史版本PostgreSQL 8.2 已于 2011 年 12 月结束社区维护,本页译文保留供仍在使用旧版本的读者参考。新系统请看当前版本。

4.2. 值表达式 #

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

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

  • 一个常量或字面值。

  • 一个列引用。

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

  • 一个下标表达式。

  • 一个字段选择表达式。

  • 一个操作符调用。

  • 一个函数调用。

  • 一个聚合表达式。

  • 一个类型转换。

  • 一个标量子查询。

  • 一个数组构造器。

  • 一个行构造器。

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

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

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

4.2.1. 列引用

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

correlation.columnname

correlation 是一个表的名字(可能带有模式名限定),或者是在 FROM 子句中为某个表定义的别名,或者是关键词 NEW 或 OLD 之一。(NEW 和 OLD 只能出现在重写规则中,而其他关联名称可以在任何 SQL 语句中使用。)如果该列名在当前查询所用的所有表中都是唯一的,则关联名称和分隔用的句点都可以省略(另见第 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]

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

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

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

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

4.2.4. 字段选择

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

expression.fieldname

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

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

(因此,限定列引用实际上只是字段选择语法的一种特例。)

4.2.5. 操作符调用

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

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

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

OPERATOR(schema.operatorname)

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

4.2.6. 函数调用

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

function ([expression [, expression ... ]] )

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

sqrt(2)

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

4.2.7. 聚合表达式 #

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

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

其中,aggregate_name 是先前定义的聚合(可以带模式名限定),expression 是任何本身不包含聚合表达式的值表达式。

第一种形式的聚合表达式在给定表达式得出非空值的所有输入行上调用聚合。(实际上,是否忽略空值取决于聚合函数本身 — 但所有标准聚合都会忽略。)第二种形式和第一种相同,因为ALL是默认选项。第三种形式为输入行中表达式的所有非空可区分值调用聚合。最后一种形式为每一个输入行调用一次聚合,无论值是否为空;因为没有特定的输入值被指定,它通常只对于count(*)聚合函数有用。

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

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

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

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

注意

PostgreSQL目前不支持将 DISTINCT 用于多于一个输入表达式。

4.2.8. 类型转换 #

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

CAST ( expression AS type )
expression::type

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

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

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

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

typename ( expression )

不过,这只对那些名称本身也可作为函数名使用的类型有效。例如,double precision 不能用这种方式,但等效的 float8 可以。另外,由于语法冲突,名称 interval、time 和 timestamp 只有在使用双引号引用时才能采用这种写法。因此,函数风格的类型转换语法会带来不一致性,在新应用中很可能应该避免使用。 (函数风格的语法事实上只是一次函数调用。当两种标准类型转换语法之一被用来做一次运行时转换时,它会在内部调用一个已注册的函数来执行该转换。按照惯例,这些转换函数与它们的输出类型同名,因此“函数风格的语法”无非是对底层转换函数的一次直接调用。显然,可移植的应用不应当依赖于这一点。)

4.2.9. 标量子查询 #

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

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

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

4.2.10. 数组构造器 #

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

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

数组元素类型是成员表达式的公共类型,其确定规则与 UNION 或 CASE 结构相同(见 第 10.5 节)。

多维数组值可以通过嵌套数组构造器来构造。在内部构造器中,关键字 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 构造。例如:

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)

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

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

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

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

4.2.11. 行构造器 #

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

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

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

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

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 NULL 或 IS 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.17 节。行构造器也可以与子查询结合使用,详见 第 9.16 节。

4.2.12. 表达式计算规则 #

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

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

SELECT true OR somefunc();

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

SELECT somefunc() OR true;

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

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

当有必要强制计算顺序时,可以使用一个CASE结构(见第 9.13 节)。例如,在一个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来回避这个问题)。

提交更正

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