值表达式用于各种上下文中,例如在 SELECT
命令的目标列表中,作为 INSERT
或 UPDATE
中的新列值,或在许多命令的搜索条件中。值表达式的结果有时被称为 标量,以将其与表表达式的结果(即表)区分开来。因此,值表达式也称为 标量表达式(或简称为 表达式)。表达式语法允许使用算术、逻辑、集合和其他运算从基本部分计算值。
值表达式是以下之一:
除了此列表之外,还有一些可以归类为表达式的结构,但它们不遵循任何一般语法规则。这些通常具有函数或运算符的语义,并在 第 9 章 的相应位置进行解释。例如,IS NULL
子句。
我们已经在 第 4.1.2 节 中讨论了常量。以下各节将讨论剩余选项。
列可以用以下形式引用:
correlation
.columnname
correlation
是表的名称(可能使用模式名称限定),或通过 FROM
子句定义的表的别名。如果列名称在当前查询中使用的所有表中都是唯一的,则可以省略相关名称和分隔点。(另请参见 第 7 章。)
位置参数引用用于指示外部提供给 SQL 语句的值。参数用于 SQL 函数定义和准备好的查询中。一些客户端库还支持单独指定数据值而不是 SQL 命令字符串,在这种情况下,参数用于引用行外数据值。参数引用的形式是
$number
例如,考虑函数 dept
的定义,如下所示:
CREATE FUNCTION dept(text) RETURNS dept AS $$ SELECT * FROM dept WHERE name = $1 $$ LANGUAGE SQL;
这里 $1
引用每次调用函数时的第一个函数参数的值。
如果表达式生成数组类型的值,则可以通过编写以下内容来提取数组值的特定元素:
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 节。
如果表达式生成复合类型(行类型)的值,则可以通过编写以下内容来提取行的特定字段:
expression
.fieldname
通常情况下,行 expression
必须用括号括起来,但是当要从中选择表达式的表达式只是一个表引用或位置参数时,可以省略括号。例如:
mytable.mycolumn $1.somecolumn (rowfunction(a,b)).col3
(因此,限定的列引用实际上只是字段选择语法的特例。)一个重要的特殊情况是从复合类型的表列中提取字段
(compositecol).somefield (mytable.compositecol).somefield
这里的括号是必需的,以表明 compositecol
是一个列名称,而不是一个表名称,或者在第二种情况下,mytable
是一个表名称,而不是一个模式名称。
您可以通过编写 .*
来请求复合值的全部字段
(compositecol).*
此符号的行为因上下文而异;有关详细信息,请参见 第 8.16.5 节。
运算符调用有两种可能的语法:
expression operator expression (二元中缀运算符) |
operator expression (一元前缀运算符) |
其中 operator
符号遵循 第 4.1.3 节 的语法规则,或者是在 AND
、OR
和 NOT
关键字中,或者是在以下形式的限定运算符名称中:
OPERATOR(
schema
.
operatorname
)
存在哪些特定的运算符以及它们是 一元还是二元,取决于系统或用户定义了哪些运算符。 第 9 章 描述了内置运算符。
函数调用的语法是函数的名称(可能使用模式名称限定),后跟括号括起来的其参数列表
function_name
([expression
[,expression
... ]] )
例如,以下计算 2 的平方根
sqrt(2)
内置函数列表在 第 9 章 中。用户可以添加其他函数。
在发出查询时,如果在某些用户不信任其他用户的数据库中,请在编写函数调用时遵守 第 10.3 节 中的安全预防措施。
参数可以可选地附加名称。有关详细信息,请参见 第 4.3 节。
接受一个复合类型单一参数的函数可以选择使用字段选择语法进行调用,反之,字段选择可以用函数式风格编写。也就是说,col(table)
和 table.col
符号是可互换的。此行为不是 SQL 标准,但在 PostgreSQL 中提供,因为它允许使用函数来模拟“计算字段”。有关更多信息,请参见 第 8.16.5 节。
聚合表达式表示跨查询选择的行应用聚合函数的结果。聚合函数将多个输入缩减为一个输出值,例如输入的总和或平均值。聚合表达式的语法如下:
aggregate_name
(expression
[ , ... ] [order_by_clause
] ) [ FILTER ( WHEREfilter_clause
) ]aggregate_name
(ALLexpression
[ , ... ] [order_by_clause
] ) [ FILTER ( WHEREfilter_clause
) ]aggregate_name
(DISTINCTexpression
[ , ... ] [order_by_clause
] ) [ FILTER ( WHEREfilter_clause
) ]aggregate_name
( * ) [ FILTER ( WHEREfilter_clause
) ]aggregate_name
( [expression
[ , ... ] ] ) WITHIN GROUP (order_by_clause
) [ FILTER ( WHEREfilter_clause
) ]
其中 aggregate_name
是先前定义的聚合函数(可能使用模式名称限定),而 expression
是任何本身不包含聚合表达式或窗口函数调用的值表达式。可选的 order_by_clause
和 filter_clause
将在下面介绍。
聚合表达式的第一种形式为每个输入行调用一次聚合函数。第二种形式与第一种相同,因为 ALL
是默认值。第三种形式为每个输入行中找到的表达式(或对于多个表达式,为每个不同的值集)的每个不同值调用一次聚合函数。第四种形式为每个输入行调用一次聚合函数;由于没有指定特定的输入值,因此它通常仅对 count(*)
聚合函数有用。最后一种形式与下面描述的 有序集 聚合函数一起使用。
大多数聚合函数忽略空输入,因此,其中一个或多个表达式产生空值的那些行将被丢弃。除非另有说明,否则可以假设所有内置聚合函数都是如此。
例如,count(*)
返回输入行的总数;count(f1)
返回 f1
非空值的输入行数,因为 count
忽略空值;而 count(distinct f1)
返回 f1
的不同非空值的数目。
通常,输入行以未指定的顺序馈送到聚合函数。在很多情况下,这并不重要;例如,min
无论以何种顺序接收输入,都会产生相同的结果。但是,某些聚合函数(如 array_agg
和 string_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
它获取表 households
中 income
列的第 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
子句中的限制适用于聚合函数所属的查询级别。
窗口函数调用 表示在查询选择的某些行上应用类聚合函数。与非窗口聚合函数调用不同,这与将选定的行分组到单个输出行无关 — 查询输出中的每一行都将保持独立。但是,窗口函数可以访问所有行,这些行将根据窗口函数调用的分组规范(PARTITION BY
列表)成为当前行的组的一部分。窗口函数调用的语法如下之一
function_name
([expression
[,expression
... ]]) [ FILTER ( WHEREfilter_clause
) ] OVERwindow_name
function_name
([expression
[,expression
... ]]) [ FILTER ( WHEREfilter_clause
) ] OVER (window_definition
)function_name
( * ) [ FILTER ( WHEREfilter_clause
) ] OVERwindow_name
function_name
( * ) [ FILTER ( WHEREfilter_clause
) ] OVER (window_definition
)
其中 window_definition
的语法为
[existing_window_name
] [ PARTITION BYexpression
[, ...] ] [ ORDER BYexpression
[ ASC | DESC | USINGoperator
] [ NULLS { FIRST | LAST } ] [, ...] ] [frame_clause
]
可选的 frame_clause
可以是以下之一
{ RANGE | ROWS | GROUPS }frame_start
[frame_exclusion
] { RANGE | ROWS | GROUPS } BETWEENframe_start
ANDframe_end
[frame_exclusion
]
其中 frame_start
和 frame_end
可以是以下之一
UNBOUNDED PRECEDINGoffset
PRECEDING CURRENT ROWoffset
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
指定构成 窗口框架的行集,该行集是当前分区的子集,用于那些对框架而不是整个分区起作用的窗口函数。框架中的行集可能会根据当前行是哪一行而有所不同。框架可以在 RANGE
、ROWS
或 GROUPS
模式下指定;在每种情况下,它都从 frame_start
运行到 frame_end
。如果省略了 frame_end
,则默认值为 CURRENT ROW
。
UNBOUNDED PRECEDING
的 frame_start
表示框架从分区的首行开始,类似地,UNBOUNDED FOLLOWING
的 frame_end
表示框架以分区的末行结束。
在 RANGE
或 GROUPS
模式下,CURRENT ROW
的 frame_start
表示框架从当前行的第一个 对等行(窗口的 ORDER BY
子句将当前行排序为等效于当前行的行)开始,而 CURRENT ROW
的 frame_end
表示框架以当前行的最后一个对等行结束。在 ROWS
模式下,CURRENT ROW
仅表示当前行。
在 offset
PRECEDING
和 offset
FOLLOWING
框架选项中, offset
必须是不包含任何变量、聚合函数或窗口函数的表达式。 offset
的含义取决于框架模式
在 ROWS
模式下, offset
必须产生非空、非负整数,该选项表示框架在当前行之前或之后开始或结束指定数量的行。
在 GROUPS
模式下,offset
必须产生一个非空且非负的整数,该选项表示帧在当前行的同类组之前或之后开始或结束指定的 同类组 数量,其中同类组是按 ORDER BY
排序等价的一组行。(在窗口定义中必须使用 ORDER BY
子句才能使用 GROUPS
模式。)
在 RANGE
模式下,这些选项要求 ORDER BY
子句精确指定一列。 offset
指定当前行中该列的值与帧中前一行或后一行中该列值的差值的最大值。 offset
表达式的类型取决于排序列的类型。对于数字排序列,它通常与排序列类型相同,但对于日期时间排序列,它是一个 interval
。例如,如果排序列的类型为 date
或 timestamp
,则可以编写 RANGE BETWEEN '1 day' PRECEDING AND '10 days' FOLLOWING
。 offset
仍然需要是非空且非负的,尽管 “非负” 的含义取决于它的数据类型。
在任何情况下,帧结束处的距离都受限于分区结束处的距离,因此对于靠近分区末尾的行,帧可能包含的行数少于其他地方。
请注意,在 ROWS
和 GROUPS
模式中,0 PRECEDING
和 0 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 FOLLOWING
, frame_end
不能是 UNBOUNDED PRECEDING
,并且 frame_end
的选择不能在上面 frame_start
和 frame_end
选项列表中比 frame_start
的选择出现得更早 - 例如 RANGE BETWEEN CURRENT ROW AND
不允许。但是,例如, offset
PRECEDINGROWS BETWEEN 7 PRECEDING AND 8 PRECEDING
是允许的,即使它永远不会选择任何行。
如果指定了 FILTER
,则只有 filter_clause
评估为真的输入行会被馈送到窗口函数;其他行将被丢弃。只有聚合的窗口函数接受 FILTER
子句。
内置窗口函数在 表 9.65 中描述。其他窗口函数可以由用户添加。此外,任何内置或用户定义的通用或统计聚合都可以用作窗口函数。(有序集和假设集聚合目前不能用作窗口函数。)
使用 *
的语法用于调用无参数聚合函数作为窗口函数,例如 count(*) OVER (PARTITION BY x ORDER BY y)
。星号 (*
) 通常不用于特定于窗口的函数。特定于窗口的函数不允许在函数参数列表中使用 DISTINCT
或 ORDER BY
。
窗口函数调用仅允许在查询的 SELECT
列表和 ORDER BY
子句中使用。
类型转换指定从一种数据类型到另一种数据类型的转换。 PostgreSQL 接受两种等效的类型转换语法
CAST (expression
AStype
)expression
::type
CAST
语法符合 SQL;使用 ::
的语法是 PostgreSQL 的历史用法。
当将转换应用于已知类型的值表达式时,它表示运行时类型转换。只有在已定义适当的类型转换操作的情况下,转换才会成功。请注意,这与在 第 4.1.2.7 节 中所示的常量中使用转换略有不同。应用于未修饰的字符串文字的转换表示将类型初始分配给文字常量值,因此它对任何类型都将成功(如果字符串文字的内容是数据类型的可接受输入语法)。
如果对值表达式必须产生的类型没有歧义(例如,当它被分配给表列时),通常可以省略显式类型转换;系统将在这种情况下自动应用类型转换。但是,自动转换仅针对系统目录中标记为 “可以隐式应用” 的转换执行。其他转换必须使用显式转换语法调用。此限制旨在防止意外转换被静默应用。
还可以使用类似函数的语法指定类型转换
typename
(expression
)
但是,这仅适用于其名称也作为函数名称有效的类型。例如, double precision
不能以这种方式使用,但等效的 float8
可以。此外,名称 interval
、time
和 timestamp
只有在用双引号括起来时才能以这种方式使用,因为存在语法冲突。因此,使用类似函数的转换语法会导致不一致,应该尽量避免。
类似函数的语法实际上只是一个函数调用。当使用两种标准转换语法之一执行运行时转换时,它将在内部调用已注册的函数来执行转换。按照惯例,这些转换函数与其输出类型同名,因此 “类似函数的语法” 仅仅是直接调用底层转换函数。显然,这并不是便携式应用程序应该依赖的。有关更多详细信息,请参阅 CREATE CAST。
COLLATE
子句会覆盖表达式的排序规则。它追加到其应用到的表达式
expr
COLLATEcollation
其中 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
数据类型。
标量子查询是在括号中的一个普通 SELECT
查询,它返回正好包含一列的一行。(有关编写查询的信息,请参阅 第 7 章。) SELECT
查询将被执行,返回的单个值将在周围的值表达式中使用。将返回多行或多列的查询用作标量子查询是一个错误。(但是,如果在特定执行过程中,子查询未返回任何行,则不会出错;标量结果将被视为 null。)子查询可以引用周围查询中的变量,这些变量在子查询的任何一次评估过程中充当常量。有关涉及子查询的其他表达式的更多信息,请参阅 第 9.24 节。
例如,以下代码查找每个州最大的城市人口
SELECT name, (SELECT max(pop) FROM cities WHERE cities.state = states.name) FROM states;
数组构造器是一个表达式,它使用其成员元素的值来构建一个数组值。简单的数组构造器由关键字 ARRAY
、左方括号 [
、数组元素值的表达式列表(用逗号分隔)以及最后的右方括号 ]
组成。例如
SELECT ARRAY[1,2,3+4]; array --------- {1,2,7} (1 row)
默认情况下,数组元素类型是成员表达式的公共类型,使用与 UNION
或 CASE
结构相同的规则确定(请参阅 第 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 节。
行构造器是一个表达式,它使用其成员字段的值构建一个行值(也称为复合值)。行构造器由关键字ROW
、左括号、零个或多个用于行字段值的表达式(用逗号隔开)以及右括号组成。例如
SELECT ROW(1,2.5,'this is a test');
当列表中有多个表达式时,关键字ROW
是可选的。
行构造器可以包含语法rowvalue
.*
,它将扩展为行值元素的列表,就像在SELECT
列表的顶层使用.*
语法时一样(参见第 8.16.5 节)。例如,如果表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)
行构造器可用于构建要存储在复合类型表列中的复合值,或要传递给接受复合参数的函数的复合值。此外,可以使用标准比较运算符测试行,如第 9.2 节中所述,将一行与另一行进行比较,如第 9.25 节中所述,并在与子查询相关的连接中使用它们,如第 9.24 节中所述,
子表达式的求值顺序未定义。特别是,运算符或函数的输入不一定是按从左到右或任何其他固定顺序求值的。
此外,如果表达式结果可以通过仅评估其某些部分来确定,则其他子表达式可能根本不会被评估。例如,如果有人写道
SELECT true OR somefunc();
那么somefunc()
(可能)根本不会被调用。如果有人写道,情况也会一样
SELECT somefunc() OR true;
请注意,这与某些编程语言中找到的布尔运算符的从左到右的“短路”不同。
因此,将带有副作用的函数用作复杂表达式的部分是不明智的。在WHERE
和HAVING
子句中依赖副作用或求值顺序尤其危险,因为这些子句在开发执行计划时会进行大量重新处理。这些子句中的布尔表达式(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()
的结果之前。相反,使用WHERE
或FILTER
子句来阻止有问题的输入行在第一时间到达聚合函数。
如果您在文档中发现任何错误,与您对特定功能的体验不符或需要进一步澄清,请使用此表格报告文档问题。