值表达式被用于各种各样的环境中,例如在SELECT命令的目标列表中,作为INSERT或UPDATE中的新列值,或者作为若干命令中的搜索条件。为了与表表达式(其结果是一张表)区分开来,值表达式的结果有时被称为标量。因此,值表达式也称为标量表达式(甚至简称为表达式)。表达式语法允许使用算术、逻辑、集合以及其他操作,从基本部分计算出值。
一个值表达式是下列之一:
在这个列表之外,还有一些结构可以被分类为一个表达式,但是它们不遵循任何一般语法规则。这些通常具有一个函数或操作符的语义并且在Chapter 9中的合适位置解释。一个示例是IS NULL子句。
我们已经在Section 4.1.2中讨论过常量。下面的小节会讨论剩下的选项。
一个列可以以下面的形式被引用:
correlation.columnname
correlation 是一个表的名字(可能带有模式名限定),或者是在 FROM 子句中为某个表定义的别名。如果该列名在当前查询所用的所有表中都是唯一的,则关联名称和分隔用的句点都可以省略(另见Chapter 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]
最后一个示例中的圆括号是必需的。详见Section 8.15。
如果一个表达式得到一个复合类型(行类型)的值,那么可以抽取该行的指定字段:
expression.fieldname
通常,行表达式必须加上圆括号,但如果要取字段的表达式只是表引用或位置参数,则圆括号可以省略。例如:
mytable.mycolumn $1.somecolumn (rowfunction(a,b)).col3
(因此,限定列引用实际上只是字段选择语法的一种特例。)一种重要的特例是从复合类型的表列中抽取字段:
(compositecol).somefield (mytable.compositecol).somefield
这里需要圆括号来显示compositecol是一个列名而不是一个表名,在第二种情况中则是显示mytable是一个表名而不是一个模式名。
你可以通过书写.*来请求一个组合值的所有字段:
(compositecol).*
这种记法的行为根据上下文会有不同,详见Section 8.16.5。
对于一次操作符调用,有两种可能的语法:
expression operator expression(二元中缀操作符) |
operator expression(一元前缀操作符) |
其中operator词元遵循Section 4.1.3的语法规则,或者是关键词AND、OR和NOT之一,或者是一个如下形式的模式限定操作符名:
OPERATOR(schema.operatorname)
哪个特定操作符存在以及它们是一元的还是二元的取决于由系统或用户定义的那些操作符。Chapter 9描述了内置操作符。
函数调用的语法是:函数名(可能带有模式名限定)后跟一组放在圆括号内的参数列表:
function_name([expression[,expression... ]] )
例如,下面会计算 2 的平方根:
sqrt(2)
内置函数的列表在Chapter 9中。其他函数可以由用户增加。
当在某些用户不信任其他用户的数据库中发出查询时,在编写函数调用时应遵守Section 10.3中的安全防范措施。
参数也可以选择附带名称。详见Section 4.3。
接受单个复合类型参数的函数,可以选择使用字段选择语法来调用;反过来,字段选择也可以写成函数风格。也就是说,col(table) 与 table.col 这两种记法可以互换。这种行为不是 SQL 标准的一部分,但 PostgreSQL 提供了它,因为这允许使用函数来模拟“计算字段”。详见Section 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 子句相同(见 Section 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)
预定义的聚合函数在Section 9.21中描述。其他聚合函数可以由用户增加。
一个聚合表达式只能出现在SELECT命令的结果列表或是HAVING子句中。在其他子句(如WHERE)中禁止使用它,因为那些子句的计算在逻辑上是在聚合的结果被形成之前。
当一个聚合表达式出现在一个子查询中(见Section 4.2.11和Section 9.24),聚合通常在该子查询的行上被计算。但是如果该聚合的参数(以及filter_clause,如果有)只包含外层变量则会产生一个异常:该聚合则属于最近的那个外层,并且会在那个查询的行上被计算。该聚合表达式从整体上则是对其所出现于的子查询的一种外层引用,并且在那个子查询的任意一次计算中都作为一个常量。只出现在结果列表或HAVING子句的限制适用于该聚合所属的查询层次。
一个窗口函数调用表示在一个查询选择的行的某个部分上应用一个聚合类的函数。和非窗口聚合函数调用不同,这不会被约束为将被选择的行分组为一个单一的输出行 — 在查询输出中每一个行仍保持独立。不过,窗口函数能够根据窗口函数调用的分组声明(PARTITION BY列表)访问属于当前行所在分组中的所有行。一个窗口函数调用的语法是下列之一:
function_name([expression[,expression... ]]) [ FILTER ( WHEREfilter_clause) ] OVERwindow_namefunction_name([expression[,expression... ]]) [ FILTER ( WHEREfilter_clause) ] OVER (window_definition)function_name( * ) [ FILTER ( WHEREfilter_clause) ] OVERwindow_namefunction_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 } BETWEEN frame_start AND frame_end [ frame_exclusion ]
其中frame_start和frame_end可以是下面形式中的一种
UNBOUNDED PRECEDINGoffsetPRECEDING CURRENT ROWoffsetFOLLOWING 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顺序中等效的行集合(要使用GROUPS模式,在窗口定义中就必须有一个ORDER BY子句)。
在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,那么只有对filter_clause计算为真的输入行会被交给该窗口函数,其他行会被丢弃。只有是聚合的窗口函数才接受FILTER 。
内置的窗口函数在Table 9.66中介绍。用户可以加入其他窗口函数。此外,任何内置的或者用户定义的通用聚合或者统计性聚合都可以被用作窗口函数(有序集和假想集聚合当前不能被用作窗口函数)。
使用*的语法被用来把参数较少的聚合函数当作窗口函数调用,例如count(*) OVER (PARTITION BY x ORDER BY y)。星号(*)通常不被用于窗口相关的函数。窗口相关的函数不允许在函数参数列表中使用DISTINCT或ORDER BY。
只有在SELECT列表和查询的ORDER BY子句中才允许窗口函数调用。
更多关于窗口函数的信息可以在Section 3.5、Section 9.22以及Section 7.2.5中找到。
一个类型转换指定从一种数据类型到另一种数据类型的转换。PostgreSQL接受两种等价的类型转换语法:
CAST (expressionAStype)expression::type
CAST语法遵从 SQL,而用::的语法是PostgreSQL的历史用法。
当类型转换被应用于一种已知类型的值表达式时,它表示一次运行时类型转换。只有在已经定义了合适的类型转换操作时,该类型转换才会成功。注意,这与常量上的类型转换(如Section 4.1.2.7中所示)略有不同。应用于未修饰字符串字面量的类型转换,表示为一个字面常量值赋予初始类型,因此它对任意类型都能成功(前提是该字符串字面量的内容符合该数据类型的输入语法)。
如果一个值表达式必须产生的类型没有歧义(例如当它被赋给一个表列时),通常可以省略显式类型转换,在这种情况下系统会自动应用类型转换。但是,自动类型转换只会对那些在系统目录中被标记为“可以隐式应用”的类型转换执行。其他类型转换必须使用显式类型转换语法来调用。施加这一限制的目的是防止出人意料的转换被悄无声息地应用。
还可以用类似函数调用的语法来指定类型转换:
typename(expression)
不过,这只对那些名称本身也可作为函数名使用的类型有效。例如,double precision 不能用这种方式,但等效的 float8 可以。另外,由于语法冲突,名称 interval、time 和 timestamp 只有在使用双引号引用时才能采用这种写法。因此,函数风格的类型转换语法会带来不一致性,通常应尽量避免。
函数风格的语法事实上只是一次函数调用。当两种标准类型转换语法之一被用来做一次运行时转换时,它将在内部调用一个已注册的函数来执行该转换。简而言之,这些转换函数具有和它们的输出类型相同的名字,并且因此“函数风格的语法”无非是对底层转换函数的一次直接调用。显然,一个可移植的应用不应当依赖于它。详见CREATE CAST。
COLLATE 子句会覆盖表达式的排序规则。它附加在所作用的表达式之后:
exprCOLLATEcollation
这里 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 子句附加到多个参数上,则会报错。详见Section 23.2。)因此,这会得到与前一个示例相同的结果:
SELECT * FROM tbl WHERE a COLLATE "C" > 'foo';
但是这是一个错误:
SELECT * FROM tbl WHERE (a > 'foo') COLLATE "C";
因为它尝试把一个排序规则应用到>操作符的结果,而它的数据类型是非可排序数据类型boolean。
标量子查询是一个放在圆括号中的普通 SELECT 查询,它恰好返回一行一列(关于如何编写查询,见Chapter 7)。该 SELECT 查询会被执行,其返回的单个值会用在外围值表达式中。把一个返回多于一行或多于一列的查询当作标量子查询来用是错误的。(但如果在某次具体执行中该子查询没有返回任何行,则不算错误;其标量结果会被视为空值。)该子查询可以引用外围查询中的变量,这些变量在该子查询的每一次求值期间都会作为常量。有关其他涉及子查询的表达式,另见Section 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 结构相同(见 Section 10.5)。你可以通过把数组构造器显式转换为所需类型来覆盖这一点,例如:
SELECT ARRAY[1,2,22.7]::integer[];
array
----------
{1,2,23}
(1 row)
这与把每个表达式分别转换为数组元素类型具有相同的效果。关于类型转换的更多信息,见 Section 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 开始。关于数组的更多信息,见Section 8.15。
行构造器是一种表达式,它使用成员字段的值来构造一个行值(也称为组合值)。行构造器由关键字 ROW、左圆括号、零个或多个用于指定行各字段值的表达式(以逗号分隔),以及最后的右圆括号组成。例如:
SELECT ROW(1,2.5,'this is a test');
当列表中有多个表达式时,关键字 ROW 是可选的。
一个行构造器可以包括语法rowvalue.*,它将被扩展为该行值的元素的一个列表,就像在一个顶层SELECT列表(见Section 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)
行构造器可用于构造复合值,以存入复合类型表列,或传递给接收复合参数的函数。 此外,还可以按 Section 9.2 所述使用标准比较操作符 测试行,按 Section 9.25 所述比较两行, 也可以按 Section 9.24 所述与子查询结合使用。
子表达式的计算顺序是未定义的。特别是,操作符或函数的输入不一定会按照从左到右或任何其他固定顺序计算。
此外,如果一个表达式的结果可以通过只计算其一部分来决定,那么其他子表达式可能完全不需要被计算。例如,如果我们写:
SELECT true OR somefunc();
那么somefunc()将(可能)完全不被调用。如果我们写成下面这样也是一样:
SELECT somefunc() OR true;
注意这和一些编程语言中布尔操作符从左至右的“短路”不同。
因此,在复杂表达式中使用带有副作用的函数是不明智的。在WHERE和HAVING子句中依赖副作用或计算顺序尤其危险,因为在建立一个执行计划时这些子句会被广泛地重新处理。这些子句中布尔表达式(AND/OR/NOT的组合)可能会以布尔代数定律所允许的任何方式被重组。
当有必要强制计算顺序时,可以使用一个CASE结构(见Section 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 并不是这类问题的万灵药。上述技术的一个限制是, 它无法阻止常量子表达式被提前计算。如Section 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子句来首先阻止有问题的输入行到达 一个聚合函数。
如果您发现文档中有不正确的内容、与您使用特定功能的经验不符或需要进一步说明,请使用此表单来报告文档问题。