2024年9月26日: PostgreSQL 17 发布!
支持版本:当前 (17) / 16 / 15 / 14 / 13 / 12
开发版本:devel
不支持版本: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

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]

(这里,方括号 [ ] 应该按字面意义出现。)每个 subscript 本身都是一个表达式,它将被四舍五入到最接近的整数值。

通常情况下,数组 expression 必须用括号括起来,但是当要带下标的表达式只是一个列引用或位置参数时,可以省略括号。此外,当原始数组是多维数组时,可以连接多个下标。例如:

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

最后一个例子中的括号是必需的。有关数组的更多信息,请参见 第 8.15 节

4.2.4. 字段选择 #

如果表达式生成复合类型(行类型)的值,则可以通过编写以下内容来提取行的特定字段:

expression.fieldname

通常情况下,行 expression 必须用括号括起来,但是当要从中选择表达式的表达式只是一个表引用或位置参数时,可以省略括号。例如:

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

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

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

这里的括号是必需的,以表明 compositecol 是一个列名称,而不是一个表名称,或者在第二种情况下,mytable 是一个表名称,而不是一个模式名称。

您可以通过编写 .* 来请求复合值的全部字段

(compositecol).*

此符号的行为因上下文而异;有关详细信息,请参见 第 8.16.5 节

4.2.5. 运算符调用 #

运算符调用有两种可能的语法:

expression operator expression (二元中缀运算符)
operator expression (一元前缀运算符)

其中 operator 符号遵循 第 4.1.3 节 的语法规则,或者是在 ANDORNOT 关键字中,或者是在以下形式的限定运算符名称中:

OPERATOR(schema.operatorname)

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

4.2.6. 函数调用 #

函数调用的语法是函数的名称(可能使用模式名称限定),后跟括号括起来的其参数列表

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

例如,以下计算 2 的平方根

sqrt(2)

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

在发出查询时,如果在某些用户不信任其他用户的数据库中,请在编写函数调用时遵守 第 10.3 节 中的安全预防措施。

参数可以可选地附加名称。有关详细信息,请参见 第 4.3 节

注意

接受一个复合类型单一参数的函数可以选择使用字段选择语法进行调用,反之,字段选择可以用函数式风格编写。也就是说,col(table)table.col 符号是可互换的。此行为不是 SQL 标准,但在 PostgreSQL 中提供,因为它允许使用函数来模拟“计算字段”。有关更多信息,请参见 第 8.16.5 节

4.2.7. 聚合表达式 #

聚合表达式表示跨查询选择的行应用聚合函数的结果。聚合函数将多个输入缩减为一个输出值,例如输入的总和或平均值。聚合表达式的语法如下:

aggregate_name (expression [ , ... ] [ order_by_clause ] ) [ FILTER ( WHERE filter_clause ) ]
aggregate_name (ALL expression [ , ... ] [ order_by_clause ] ) [ FILTER ( WHERE filter_clause ) ]
aggregate_name (DISTINCT expression [ , ... ] [ order_by_clause ] ) [ FILTER ( WHERE filter_clause ) ]
aggregate_name ( * ) [ FILTER ( WHERE filter_clause ) ]
aggregate_name ( [ expression [ , ... ] ] ) WITHIN GROUP ( order_by_clause ) [ FILTER ( WHERE filter_clause ) ]

其中 aggregate_name 是先前定义的聚合函数(可能使用模式名称限定),而 expression 是任何本身不包含聚合表达式或窗口函数调用的值表达式。可选的 order_by_clausefilter_clause 将在下面介绍。

聚合表达式的第一种形式为每个输入行调用一次聚合函数。第二种形式与第一种相同,因为 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 节 所述,但其表达式始终只是表达式,不能是输出列名或编号。例如

WITH vals (v) AS ( VALUES (1),(3),(4),(3),(2) )
SELECT array_agg(v ORDER BY v DESC) FROM vals;
  array_agg
-------------
 {4,3,3,2,1}

由于 jsonb 仅保留最后一个匹配的键,因此其键的排序可能很重要

WITH vals (k, v) AS ( VALUES ('key0','1'), ('key1','3'), ('key1','2') )
SELECT jsonb_object_agg(k, v ORDER BY v) FROM vals;
      jsonb_object_agg
----------------------------
 {"key0": "1", "key1": "3"}

在处理多参数聚合函数时,请注意 ORDER BY 子句位于所有聚合参数之后。例如,请编写以下内容

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

而不是以下内容

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

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

如果在 order_by_clause 中指定了 DISTINCT,则 ORDER BY 表达式只能引用 DISTINCT 列表中的列。例如

WITH vals (v) AS ( VALUES (1),(3),(4),(3),(2) )
SELECT array_agg(DISTINCT v ORDER BY v DESC) FROM vals;
 array_agg
-----------
 {4,3,2,1}

如前所述,将 ORDER BY 放在聚合函数的常规参数列表中,用于为通用的和统计的聚合函数排序输入行,对于这些聚合函数来说,排序是可选的。聚合函数有一个子类,称为 有序集聚合函数,它们需要一个 order_by_clause,因为聚合函数的计算通常只有在输入行的特定排序下才有意义。有序集聚合函数的典型示例包括排名和百分位计算。对于有序集聚合函数, order_by_clause 写在 WITHIN GROUP (...) 中,如上面的最终语法替代方案所示。 order_by_clause 中的表达式为每个输入行评估一次,就像常规聚合函数参数一样,根据 order_by_clause 的要求进行排序,并作为输入参数馈送到聚合函数中。(这与非 WITHIN GROUP order_by_clause 不同,后者不被视为聚合函数的参数。)出现在 WITHIN GROUP 之前的参数表达式(如果有)被称为聚合函数的 直接参数,以区别于 order_by_clause 中列出的 聚合参数。与常规聚合函数参数不同,直接参数仅在每次聚合函数调用时评估一次,而不是为每个输入行评估一次。这意味着,它们只能包含变量,前提是这些变量已通过 GROUP BY 进行分组;这种限制与直接参数不在聚合表达式中时相同。直接参数通常用于百分位分数等内容,这些内容只有作为每次聚合计算的单个值才说得通。直接参数列表可以为空;在这种情况下,只需编写 (),而不是 (*)。(PostgreSQL 实际上接受这两种写法,但只有第一种写法符合 SQL 标准。)

有序集聚合函数调用的示例如下

SELECT percentile_cont(0.5) WITHIN GROUP (ORDER BY income) FROM households;
 percentile_cont
-----------------
           50489

它获取表 householdsincome 列的第 50 个百分位数或中位数。这里,0.5 是直接参数;百分位分数随行变化是没有意义的。

如果指定了 FILTER,则只有 filter_clause 评估为真的输入行才会馈送到聚合函数;其他行将被丢弃。例如

SELECT
    count(*) AS unfiltered,
    count(*) FILTER (WHERE i < 5) AS filtered
FROM generate_series(1,10) AS s(i);
 unfiltered | filtered
------------+----------
         10 |        4
(1 row)

预定义的聚合函数在 第 9.21 节 中进行了描述。用户可以添加其他聚合函数。

聚合表达式只能出现在 SELECT 命令的结果列表或 HAVING 子句中。它在其他子句(如 WHERE)中是被禁止的,因为这些子句在逻辑上是在形成聚合结果之前进行评估的。

当聚合表达式出现在子查询中时(请参阅 第 4.2.11 节第 9.24 节),聚合函数通常会针对子查询的行进行评估。但如果聚合函数的参数(以及任何 filter_clause)仅包含外部级别变量,则会出现一个例外:聚合函数会属于最近的外部级别,并针对该查询的行进行评估。然后,作为整体的聚合表达式将是它所在的子查询的外部引用,并且在该子查询的任何一次评估中都充当常量。关于只能出现在结果列表或 HAVING 子句中的限制适用于聚合函数所属的查询级别。

4.2.8. 窗口函数调用 #

窗口函数调用 表示在查询选择的某些行上应用类聚合函数。与非窗口聚合函数调用不同,这与将选定的行分组到单个输出行无关 — 查询输出中的每一行都将保持独立。但是,窗口函数可以访问所有行,这些行将根据窗口函数调用的分组规范(PARTITION BY 列表)成为当前行的组的一部分。窗口函数调用的语法如下之一

function_name ([expression [, expression ... ]]) [ FILTER ( WHERE filter_clause ) ] OVER window_name
function_name ([expression [, expression ... ]]) [ FILTER ( WHERE filter_clause ) ] OVER ( window_definition )
function_name ( * ) [ FILTER ( WHERE filter_clause ) ] OVER window_name
function_name ( * ) [ FILTER ( WHERE filter_clause ) ] 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 | GROUPS } frame_start [ frame_exclusion ]
{ RANGE | ROWS | GROUPS } BETWEEN frame_start AND frame_end [ frame_exclusion ]

其中 frame_startframe_end 可以是以下之一

UNBOUNDED PRECEDING
offset PRECEDING
CURRENT ROW
offset FOLLOWING
UNBOUNDED FOLLOWING

frame_exclusion 可以是以下之一

EXCLUDE CURRENT ROW
EXCLUDE GROUP
EXCLUDE TIES
EXCLUDE NO OTHERS

这里, expression 代表任何本身不包含窗口函数调用的值表达式。

window_name 是对在查询的 WINDOW 子句中定义的命名窗口规范的引用。或者,可以使用与在 WINDOW 子句中定义命名窗口相同的语法,在括号中提供完整的 window_definition;有关详细信息,请参阅 SELECT 参考页面。值得指出的是,OVER wname 并不完全等同于 OVER (wname ...);后者表示复制和修改窗口定义,如果引用的窗口规范包含框架子句,则将被拒绝。

PARTITION BY 子句将查询的行分组到 分区中,这些分区由窗口函数单独处理。 PARTITION BY 的工作方式类似于查询级别的 GROUP BY 子句,但其表达式始终只是表达式,不能是输出列名或编号。如果没有 PARTITION BY,则查询产生的所有行将被视为单个分区。 ORDER BY 子句确定窗口函数处理分区中的行时的顺序。它的工作方式类似于查询级别的 ORDER BY 子句,但同样不能使用输出列名或编号。如果没有 ORDER BY,则行将以未指定的顺序进行处理。

frame_clause 指定构成 窗口框架的行集,该行集是当前分区的子集,用于那些对框架而不是整个分区起作用的窗口函数。框架中的行集可能会根据当前行是哪一行而有所不同。框架可以在 RANGEROWSGROUPS 模式下指定;在每种情况下,它都从 frame_start 运行到 frame_end。如果省略了 frame_end,则默认值为 CURRENT ROW

UNBOUNDED PRECEDINGframe_start 表示框架从分区的首行开始,类似地,UNBOUNDED FOLLOWINGframe_end 表示框架以分区的末行结束。

RANGEGROUPS 模式下,CURRENT ROWframe_start 表示框架从当前行的第一个 对等行(窗口的 ORDER BY 子句将当前行排序为等效于当前行的行)开始,而 CURRENT ROWframe_end 表示框架以当前行的最后一个对等行结束。在 ROWS 模式下,CURRENT ROW 仅表示当前行。

offset PRECEDINGoffset FOLLOWING 框架选项中, offset 必须是不包含任何变量、聚合函数或窗口函数的表达式。 offset 的含义取决于框架模式

  • ROWS 模式下, offset 必须产生非空、非负整数,该选项表示框架在当前行之前或之后开始或结束指定数量的行。

  • GROUPS 模式下,offset 必须产生一个非空且非负的整数,该选项表示帧在当前行的同类组之前或之后开始或结束指定的 同类组 数量,其中同类组是按 ORDER BY 排序等价的一组行。(在窗口定义中必须使用 ORDER BY 子句才能使用 GROUPS 模式。)

  • RANGE 模式下,这些选项要求 ORDER BY 子句精确指定一列。 offset 指定当前行中该列的值与帧中前一行或后一行中该列值的差值的最大值。 offset 表达式的类型取决于排序列的类型。对于数字排序列,它通常与排序列类型相同,但对于日期时间排序列,它是一个 interval。例如,如果排序列的类型为 datetimestamp,则可以编写 RANGE BETWEEN '1 day' PRECEDING AND '10 days' FOLLOWINGoffset 仍然需要是非空且非负的,尽管 非负 的含义取决于它的数据类型。

在任何情况下,帧结束处的距离都受限于分区结束处的距离,因此对于靠近分区末尾的行,帧可能包含的行数少于其他地方。

请注意,在 ROWSGROUPS 模式中,0 PRECEDING0 FOLLOWING 等价于 CURRENT ROW。这通常也适用于 RANGE 模式,对于 的特定数据类型含义。

frame_exclusion 选项允许将当前行周围的行从帧中排除,即使它们会根据帧开始和帧结束选项包含在内。 EXCLUDE CURRENT ROW 将当前行从帧中排除。 EXCLUDE GROUP 将当前行及其排序同类组从帧中排除。 EXCLUDE TIES 将当前行的所有同类组从帧中排除,但不排除当前行本身。 EXCLUDE NO OTHERS 只显式指定不排除当前行或其同类组的默认行为。

默认的帧选项是 RANGE UNBOUNDED PRECEDING,它与 RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW 相同。使用 ORDER BY,这将帧设置为从分区开始到当前行的最后一个 ORDER BY 同类组的所有行。如果没有 ORDER BY,这意味着分区中的所有行都包含在窗口帧中,因为所有行都成为当前行的同类组。

限制是 frame_start 不能是 UNBOUNDED FOLLOWINGframe_end 不能是 UNBOUNDED PRECEDING,并且 frame_end 的选择不能在上面 frame_startframe_end 选项列表中比 frame_start 的选择出现得更早 - 例如 RANGE BETWEEN CURRENT ROW AND offset PRECEDING 不允许。但是,例如, ROWS BETWEEN 7 PRECEDING AND 8 PRECEDING 是允许的,即使它永远不会选择任何行。

如果指定了 FILTER,则只有 filter_clause 评估为真的输入行会被馈送到窗口函数;其他行将被丢弃。只有聚合的窗口函数接受 FILTER 子句。

内置窗口函数在 表 9.65 中描述。其他窗口函数可以由用户添加。此外,任何内置或用户定义的通用或统计聚合都可以用作窗口函数。(有序集和假设集聚合目前不能用作窗口函数。)

使用 * 的语法用于调用无参数聚合函数作为窗口函数,例如 count(*) OVER (PARTITION BY x ORDER BY y)。星号 (*) 通常不用于特定于窗口的函数。特定于窗口的函数不允许在函数参数列表中使用 DISTINCTORDER BY

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

有关窗口函数的更多信息,请参阅 第 3.5 节第 9.22 节第 7.2.5 节

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 子句附加到多个参数是一个错误。有关更多详细信息,请参阅 第 23.2 节。)因此,这与前面的示例具有相同的结果

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

但这是错误的

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

因为它试图将排序规则应用于 > 运算符的结果,该结果是非排序规则的 boolean 数据类型。

4.2.11. 标量子查询 #

标量子查询是在括号中的一个普通 SELECT 查询,它返回正好包含一列的一行。(有关编写查询的信息,请参阅 第 7 章。) SELECT 查询将被执行,返回的单个值将在周围的值表达式中使用。将返回多行或多列的查询用作标量子查询是一个错误。(但是,如果在特定执行过程中,子查询未返回任何行,则不会出错;标量结果将被视为 null。)子查询可以引用周围查询中的变量,这些变量在子查询的任何一次评估过程中充当常量。有关涉及子查询的其他表达式的更多信息,请参阅 第 9.24 节

例如,以下代码查找每个州最大的城市人口

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}
(1 row)

SELECT ARRAY(SELECT ARRAY[i, i*2] FROM generate_series(1,5) AS a(i));
              array
----------------------------------
 {{1,2},{2,4},{3,6},{4,8},{5,10}}
(1 row)

子查询必须返回一列。如果子查询的输出列是非数组类型,则生成的单维数组将为子查询结果中的每一行提供一个元素,元素类型与子查询的输出列匹配。如果子查询的输出列是数组类型,则结果将是相同类型但维度高一级的数组;在这种情况下,所有子查询行必须生成具有相同维度的数组,否则结果将不是矩形的。

使用ARRAY构建的数组值的索引始终从 1 开始。有关数组的更多信息,请参见第 8.15 节

4.2.13. 行构造器 #

行构造器是一个表达式,它使用其成员字段的值构建一个行值(也称为复合值)。行构造器由关键字ROW、左括号、零个或多个用于行字段值的表达式(用逗号隔开)以及右括号组成。例如

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

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

行构造器可以包含语法rowvalue.*,它将扩展为行值元素的列表,就像在SELECT列表的顶层使用.*语法时一样(参见第 8.16.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)

行构造器可用于构建要存储在复合类型表列中的复合值,或要传递给接受复合参数的函数的复合值。此外,可以使用标准比较运算符测试行,如第 9.2 节中所述,将一行与另一行进行比较,如第 9.25 节中所述,并在与子查询相关的连接中使用它们,如第 9.24 节中所述,

4.2.14. 表达式求值规则 #

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

此外,如果表达式结果可以通过仅评估其某些部分来确定,则其他子表达式可能根本不会被评估。例如,如果有人写道

SELECT true OR somefunc();

那么somefunc()(可能)根本不会被调用。如果有人写道,情况也会一样

SELECT somefunc() OR true;

请注意,这与某些编程语言中找到的布尔运算符的从左到右的短路不同。

因此,将带有副作用的函数用作复杂表达式的部分是不明智的。在WHEREHAVING子句中依赖副作用或求值顺序尤其危险,因为这些子句在开发执行计划时会进行大量重新处理。这些子句中的布尔表达式(AND/OR/NOT 组合)可以以布尔代数定律允许的任何方式重新组织。

当必须强制求值顺序时,可以使用CASE结构(参见第 9.18 节)。例如,这是一种不值得信赖的方式,试图避免在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并不是解决此类问题的万能药。上面说明的技术的一个局限性是它不能阻止对常量子表达式的早期评估。如第 36.7 节所述,标记为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()的结果之前。相反,使用WHEREFILTER子句来阻止有问题的输入行在第一时间到达聚合函数。

提交更正

如果您在文档中发现任何错误,与您对特定功能的体验不符或需要进一步澄清,请使用此表格报告文档问题。