值表达式用于各种上下文中,例如在于 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
]
(此处,尖括号 [ ]
表示应按字面意思出现。)每个 下标
本身就是一个表达式,它将被舍入到最接近的整数。
一般来说,数组 表达式
必须用括号括起来,但在要带下标的表达式只是一个列引用或位置参数时,可以省略括号。此外,当原始数组是多维数组时,可以连接多个下标。例如
mytable.arraycolumn[4] mytable.two_d_column[17][34] $1[10:42] (arrayfunction(a,b))[42]
最近示例中的括号是必需的。有关数组的更多信息,请参见 第 8.15 节。
如果某个表达式产生一个复合类型的值(行类型),那么可以通过编写来抽取该行的一个特定字段
expression
.fieldname
一般来说,行 表达式
必须用括号括起来,但在要从中选择的表达式只是一个表引用或位置参数时,可以省略括号。例如
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
键的单参数聚合函数的调用(第二个键相当无用,因为它是一个常量)。
如果 DISTINCT
是通过 order_by_clause
指定的,那么 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
子句中定义的命名窗口规范的引用。或者,可以使用与 WINDOW
子句中定义命名窗口时相同的语法,在括号中给出完整的 窗口定义
;有关详细信息,请参阅 SELECT 参考页面。值得指出的是,OVER wname
与 OVER (wname ...)
并不完全等同;后者暗示复制和修改窗口定义,并且如果引用的窗口规范包含帧子句,将被拒绝。
PARTITION BY
子句将查询的行分组为窗口函数分别处理的分区。PARTITION BY
的工作方式类似于查询级别的 GROUP BY
子句,不同之处在于它的表达式始终只是表达式,不能是输出列名称或数字。如果没有 PARTITION BY
,则由查询生成的全部行将被视为单个分区。ORDER BY
子句确定窗口函数处理分区中各行的顺序。它的工作方式类似于查询级别的 ORDER BY
子句,但同样不能使用输出列名称或数字。如果没有 ORDER BY
,则按未指定顺序处理行。
帧子句
指定对那些以帧而不是整个分区进行操作的窗口函数而言的窗口帧的行集,而窗口帧是当前分区的子集。帧中的行集会随着哪行是当前行而有所不同。帧可在 RANGE
、ROWS
或 GROUPS
模式中指定;在每种情况下,它从帧开始
运行到帧结束
。如果省略帧结束
,则结束位置默认为 CURRENT ROW
。
UNBOUNDED PRECEDING
的帧开始
意味着帧从分区的首行开始,类似地,UNBOUNDED FOLLOWING
的帧结束
意味着帧以分区的末行结束。
在 RANGE
或 GROUPS
模式下,CURRENT ROW
的帧开始
意味着帧从当前行的第一个对等行开始(窗口的 ORDER BY
子句将其分类为等同于当前行的行),而帧结束
的 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_start
和 frame_end
选项列表中的 frame_end
选项不能出现在 frame_start
选项之前,例如 RANGE BETWEEN CURRENT ROW AND
不被允许。但是,例如,offset
PRECEDINGROWS BETWEEN 7 PRECEDING AND 8 PRECEDING
是被允许的,即使它永远不会选择任何行。
如果指定了 FILTER
,则仅当为其评估为 true 的 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 节 中所示。应用于未修饰字符串文字的转换表示对文字常量值的初始分配类型,因此它适用于任何类型(如果字符串文字的内容是对数据类型可接受的输入语法)。
如果对值表达式必须生成什么类型没有歧义(例如,当它分配给表列时),通常可以省略显式类型转换;这种情况下,系统将自动应用类型转换。但是,仅对在系统目录中标记为 “OK 隐式应用” 的转换执行自动转换。其他转换必须使用显式转换语法调用。此限制旨在防止令人惊讶的转换被默认应用。
还可以使用类似函数的语法指定类型转换
typename
(expression
)
但是,这仅适用于名称也作为函数名称有效的类型。例如,double precision
不能这么用,但等效的 float8
可以。此外,名称 interval
、time
和 timestamp
只有在使用双引号的情况下才能这样用,以避免语法冲突。因此,使用类似函数的强制转换语法会导致不一致,并且应避免使用。
类似函数的语法实际上只是一个函数调用。如果使用两种标准强制转换语法中的一种来执行运行时转换,它将内部调用注册的函数来执行转换。根据惯例,这些转换函数的名称与它们的输出类型相同,因此“类似函数的语法”仅仅是底层转换函数的直接调用。显然,便携式应用程序不应该依赖它。如需更多详情,请参阅 CREATE CAST。
COLLATE
子句覆盖表达式的排序规则。它附加到它应用到的表达式
expr
COLLATEcollation
其中 排序规则
是一个可能的模式限定标识符。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
构建的数组值的脚标始终从一开头。有关数组的更多信息,请参见第 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
子句中的其他表达式之前,先计算 CASE
中的聚集表达式。例如,以下查询可导致除以零错误,尽管它似乎已经进行了保护
SELECT CASE WHEN min(employees) > 0 THEN avg(expenses / employees) END FROM departments;
min()
和 avg()
聚集将在所有输入行上同时计算,因此如果任何一行都有 employees
等于零,那么在有机会测试 min()
的结果之前就会发生除以零错误。相反,可以使用 WHERE
或 FILTER
子句,以防止有问题的输入行在第一位到达聚集函数。