值表达式用于多种上下文,例如在 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
计算结果为 true 的输入行才会馈送到聚合函数;其他行会被丢弃。例如:
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_definition
,使用与在 WINDOW
子句中定义命名窗口相同的语法;有关详细信息,请参阅 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
。
frame_start
为 UNBOUNDED PRECEDING
表示帧从分区的首行开始,类似地,frame_end
为 UNBOUNDED FOLLOWING
表示帧以分区的最后一行结束。
在 RANGE
或 GROUPS
模式下,frame_start
为 CURRENT ROW
表示帧从当前行的第一个 对等 行(窗口的 ORDER BY
子句排序时与当前行等效的行)开始,而 frame_end
为 CURRENT ROW
表示帧以当前行的最后一个对等行结束。在 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_start
和 frame_end
选项列表中更早出现 — 例如,不允许使用 RANGE BETWEEN CURRENT ROW AND
。但是,例如,允许使用 offset
PRECEDINGROWS BETWEEN 7 PRECEDING AND 8 PRECEDING
,即使它永远不会选择任何行。
如果指定了 FILTER
,则只有 filter_clause
计算结果为 true 的输入行才会被馈送到窗口函数;其他行将被丢弃。只有作为聚合的窗口函数才接受 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
子句来防止有问题的输入行首先到达聚合函数。
如果您在文档中发现任何不正确、与您使用特定功能的经验不符或需要进一步澄清的地方,请使用此表单报告文档问题。