4.2. 值表达式

值表达式在各种上下文中使用,例如在SELECT命令的目标列表中,在INSERTUPDATE中用作新的列值,或在许多命令中用于搜索条件。值表达式的结果有时称为* scalar ,以将其与表表达式(即表)的结果区分开。因此,值表达式也称为标量表达式*(甚至简称为* expressions *)。表达式语法允许使用算术,逻辑,集合和其他运算从原始部分计算值。

值表达式是以下之一:

  • 常数或 Literals 值

  • 列参考

  • 在函数定义或准备好的语句主体中的位置参数引用

  • 下标表达式

  • 字段选择表达式

  • 操作员调用

  • 函数调用

  • 聚合表达式

  • 窗口函数调用

  • 类型转换

  • 排序规则表达式

  • 标量子查询

  • 数组构造函数

  • 行构造器

  • 括号中的另一个值表达式(用于对子表达式进行分组并覆盖优先级)

除了此列表外,还有许多可归类为表达式但不遵循任何常规语法规则的构造。这些通常具有函数或运算符的语义,并在Chapter 9的适当位置中进行了说明。一个示例是IS NULL子句。

我们已经在Section 4.1.2中讨论了常量。以下各节讨论其余选项。

4 .2.1. 列引用

可以采用以下形式引用列:

correlation.columnname
  • correlation *是表的名称(可能用模式名称限定),或者是通过FROM子句定义的表的别名。如果列名称在当前查询中使用的所有表中都是唯一的,则可以省略相关名称和分隔点。 (另请参见Chapter 7。)

4 .2.2. 位置参数

位置参数引用用于指示从外部提供给 SQL 语句的值。参数用于 SQL 函数定义和准备好的查询中。某些 Client 端库还支持与 SQL 命令字符串分开指定数据值,在这种情况下,参数用于引用离线数据值。参数引用的形式为:

$number

例如,将函数dept的定义考虑为:

CREATE FUNCTION dept(text) RETURNS dept
    AS $$ SELECT * FROM dept WHERE name = $1 $$
    LANGUAGE SQL;

每次调用函数时,$1都会引用第一个函数参数的值。

4.2.3. Subscripts

如果表达式产生数组类型的值,则可以通过写来提取数组值的特定元素

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]

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

4 .2.4. site 选择

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

expression.fieldname

通常,行* expression *必须带括号,但是当要选择的表达式只是表引用或位置参数时,可以省略括号。例如:

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

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

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

在第二种情况下,此处需要使用括号以表明compositecol是列名而不是表名,或者mytable是表名而不是架构名。

您可以通过写入.*来请求复合值的所有字段:

(compositecol).*

根据上下文的不同,此表示法的行为也有所不同。有关详情,请参见Section 8.16.5

4 .2.5. 操作员调用

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

* expression * * operator * * expression *(二进制中缀运算符)
* operator * * expression *(一元前缀运算符)
* expression * * operator *(一元后缀运算符)

其中* operator *令牌遵循Section 4.1.3的语法规则,或者是关键字ANDORNOT之一,或者是格式为合格的运算符名称:

OPERATOR(schema.operatorname)

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

4 .2.6. 函数调用

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

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

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

sqrt(2)

内置函数的列表在Chapter 9中。用户可以添加其他功能。

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

参数可以选择附加名称。有关详情,请参见Section 4.3

Note

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

4 .2.7. 汇总表达式

聚合表达式表示在查询选择的行上应用聚合函数。聚合函数将多个 Importing 减少为单个输出值,例如 Importing 的总和或平均值。聚合表达式的语法是以下之一:

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_clause filter_clause *如下所述。

聚合表达式的第一种形式为每个 Importing 行调用一次聚合。第二种形式与第一种形式相同,因为默认值为ALL。第三种形式为在 Importing 行中找到的表达式的每个不同值(或多个表达式的不同值集)调用一次聚合。第四种形式为每个 Importing 行调用一次聚合;由于未指定特定的 Importing 值,因此通常仅对count(*)聚合函数有用。最后一种形式与* ordered-set *聚合函数一起使用,如下所述。

大多数聚合函数会忽略空 Importing,因此将丢弃其中一个或多个表达式产生空值的行。除非另有说明,否则对于所有内置聚合,可以假定这是正确的。

例如,count(*)产生 Importing 行的总数; count(f1)产生f1不为空的 Importing 行数,因为count忽略空值;和count(distinct f1)产生f1的不同非空值的数量。

通常,Importing 行以未指定的 Sequences 馈送到聚合函数。在许多情况下,这无关紧要;例如,min不管接收 Importing 的 Sequences 如何,都会产生相同的结果。但是,某些聚合函数(例如array_aggstring_agg)产生的结果取决于 Importing 行的 Sequences。使用这种聚合时,可以使用可选的* order_by_clause *指定所需的 Sequences。 * order_by_clause *具有与查询级ORDER BY子句相同的语法,如Section 7.5中所述,不同之处在于其表达式始终只是表达式,不能是输出列名称或数字。例如:

SELECT array_agg(a ORDER BY b DESC) FROM table;

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

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

not this:

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

后者在语法上是有效的,但它表示使用两个ORDER BY键的单参数聚合函数的调用(第二个键是常数,因此相当没用)。

如果除* order_by_clause *之外还指定了DISTINCT,则所有ORDER BY表达式必须匹配聚合的正则参数;否则,也就是说,您不能对DISTINCT列表中未包含的表达式进行排序。

Note

在聚合函数中同时指定DISTINCTORDER BY的功能是 PostgreSQL 扩展。

如前所述,将ORDER BY放在聚合的常规参数列表中时,是在对通用和统计聚合的 Importing 行进行排序时使用该排序(可选)。有一个聚合函数的子类,称为有序集聚合,* 必填 order_by_clause ,这通常是因为聚合的计算仅在其 Importing 行的特定排序方面才有意义。有序集合聚合的典型示例包括等级和百分位计算。对于有序集合, order_by_clause 写在WITHIN GROUP (...)内,如上面的最终语法替代所示。 * order_by_clause 中的表达式像常规聚合参数一样在每个 Importing 行中被评估一次,按照 order_by_clause 的要求进行排序,并作为 Importing 参数馈入聚合函数。 (这与非WITHIN GROUP * order_by_clause 的情况不同,后者不会被视为聚合函数的参数.)WITHIN GROUP之前的参数表达式(如果有的话)称为直接参数,以区别于它们。 * order_by_clause 中列出的汇总参数。与常规聚合参数不同,直接参数对每个聚合调用仅求值一次,而不对每个 Importing 行求值一次。这意味着仅当这些变量按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 的 Importing 行馈送到聚合函数;其他行将被丢弃。例如:

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)

sched 义的聚合函数在Section 9.20中描述。用户可以添加其他聚合函数。

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

当聚合表达式出现在子查询中时(请参见Section 4.2.11Section 9.22),通常会在子查询的行上对聚合进行求值。但是,如果聚合的参数(和* 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 } frame_start
{ RANGE | ROWS } BETWEEN frame_start AND frame_end

其中* frame_start frame_end *可以是以下之一

UNBOUNDED PRECEDING
value PRECEDING
CURRENT ROW
value FOLLOWING
UNBOUNDED FOLLOWING

在此,* expression *表示本身不包含窗口函数调用的任何值表达式。

  • window_name *是对查询的WINDOW子句中定义的命名窗口规范的引用。另外,也可以使用与在WINDOW子句中定义命名窗口相同的语法在括号内给出完整的_window_definition *。有关详细信息,请参见SELECT参考页。值得指出的是OVER wname并不完全等同于OVER (wname ...);后者意味着复制和修改窗口定义,如果所引用的窗口规范包含 frame 子句,则将被拒绝。

PARTITION BY子句将查询的行分为* partitions *,由 window 函数分别处理。 PARTITION BY与查询级GROUP BY子句的工作方式类似,不同之处在于,它的表达式始终只是表达式,不能作为输出列名称或数字。如果没有PARTITION BY,那么查询产生的所有行将被视为单个分区。 ORDER BY子句确定窗口函数处理分区行的 Sequences。它的工作方式类似于查询级ORDER BY子句,但同样不能使用输出列名称或数字。没有ORDER BY,将以未指定的 Sequences 处理行。

  • frame_clause 指定构成 window frame 的行集,该窗口集是当前分区的子集,用于那些作用于框架而不是整个分区的窗口函数。可以在RANGEROWS模式下指定帧。无论哪种情况,它都从 frame_start frame_end 运行。如果省略 frame_end *,则默认为CURRENT ROW

UNBOUNDED PRECEDING的* frame_start 表示帧以分区的第一行开始,类似地,UNBOUNDED FOLLOWING frame_end *表示帧以分区的最后一行结束。

RANGE模式下,CURRENT ROW的* frame_start 表示帧以当前行的第一对等行(ORDER BY认为与当前行等效的行)开始,而CURRENT ROW的* frame_end *表示帧以最后一行结束等效的ORDER BY同级。在ROWS模式下,CURRENT ROW只是表示当前行。

当前仅在ROWS模式下允许* value * PRECEDING和* value * FOLLOWING情况。它们指示帧在当前行之前或之后指定的行数开始或结束。 * value *必须是不包含任何变量,聚合函数或窗口函数的整数表达式。该值不能为 null 或负数;但可以为零,仅选择当前行。

默认的取景选项为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 *选择出现在上述列表中-例如,不允许RANGE BETWEEN CURRENT ROW AND value PRECEDING

如果指定了FILTER,则仅将* filter_clause *评估为 true 的 Importing 行馈送到 window 函数;其他行将被丢弃。只有聚合的窗口函数才接受FILTER子句。

Table 9.57中描述了内置的窗口功能。用户可以添加其他窗口功能。同样,任何内置或用户定义的通用或统计聚合都可以用作窗口函数。 (有序集和假设集的集合目前不能用作窗口函数.)

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

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

有关窗口功能的更多信息,请参见Section 3.5Section 9.21Section 7.2.5

4 .2.9. 类型转换

类型转换指定从一种数据类型到另一种数据类型的转换。 PostgreSQL 接受两种等效的类型转换语法:

CAST ( expression AS type )
expression::type

CAST语法符合 SQL; ::的语法是 PostgreSQL 的历史用法。

将强制转换应用于已知类型的值表达式时,它表示运行时类型转换。仅在定义了适当的类型转换操作后,转换才会成功。请注意,这与使用常量进行强制类型转换完全不同,如Section 4.1.2.7所示。应用于未经修饰的字符串 Literals 的强制转换表示将类型初始分配给 Literals 常量值,因此对于任何类型,该转换都将成功(如果字符串 Literals 的内容对于数据类型而言是可接受的 Importing 语法)。

如果对值表达式必须产生的类型没有歧义(例如,当将其分配给表列时),则通常可以省略显式类型转换。在这种情况下,系统会自动应用类型转换。但是,仅对系统目录中标记为“可以隐式应用”的强制转换进行自动强制转换。其他转换必须使用显式转换语法来调用。此限制旨在防止意外地进行无提示的转换。

也可以使用类似函数的语法来指定类型转换:

typename ( expression )

但是,这仅适用于名称也可用作函数名称的类型。例如,不能以这种方式使用double precision,但是可以使用等效的float8。另外,由于语法冲突,如果名称intervaltimetimestamp被双引号引起来,则只能以这种方式使用。因此,使用类似函数的强制转换语法会导致不一致,应避免使用。

Note

类似函数的语法实际上只是一个函数调用。当使用两种标准强制转换语法之一进行运行时转换时,它将在内部调用注册的函数来执行转换。按照惯例,这些转换函数与其输出类型具有相同的名称,因此“类似于函数的语法”无非就是直接调用基础转换函数。显然,这不是便携式应用程序应该依赖的东西。有关更多详细信息,请参见CREATE CAST

4 .2.10. 归类表达式

COLLATE子句覆盖表达式的排序规则。它附加到适用于的表达式:

expr COLLATE collation

其中* collation *是可能是模式限定的标识符。 COLLATE子句的绑定比运算符更紧密;必要时可以使用括号。

如果未明确指定排序规则,则数据库系统要么从表达式中涉及的列中得出排序规则,要么默认为数据库的默认排序规则(如果表达式中不涉及任何列)。

COLLATE子句的两个常见用法是覆盖ORDER BY子句中的排序 Sequences,例如:

SELECT a, b, c FROM tbl WHERE ... ORDER BY a COLLATE "C";

并覆盖对语言环境敏感的结果的函数或运算符调用的排序规则,例如:

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

请注意,在后一种情况下,COLLATE子句附加到我们希望影响的运算符的 Importing 参数上。附加COLLATE子句的运算符或函数的哪个参数无关紧要,因为运算符或函数所应用的排序规则是通过考虑所有参数而派生的,而显式的COLLATE子句将覆盖所有其他的排序规则论点。 (但是,将不匹配的COLLATE子句附加到多个参数中是一个错误.有关更多详细信息,请参见Section 23.2。)因此,这得到的结果与前面的示例相同:

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

但这是一个错误:

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

因为它尝试将归类应用于>运算符的结果,该运算符的数据类型为boolean

4 .2.11. 标量子查询

标量子查询是括号中的普通SELECT查询,该查询恰好返回一行加一列。 (有关编写查询的信息,请参见Chapter 7。)执行SELECT查询,并在周围的值表达式中使用单个返回的值。使用返回多于一行或多于一列的查询作为标量子查询是错误的。 (但是,如果在特定的执行过程中,子查询不返回任何行,则没有错误;标量结果被视为空.)子查询可以引用周围查询中的变量,该变量将在任何一次求值期间充当常量子查询。有关涉及子查询的其他表达式,另请参见Section 9.22

例如,以下内容查找每个 State 的最大城市人口:

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构造相同的规则确定(请参见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构造函数的任何强制转换都会自动传播到所有内部构造函数。

多维数组构造器元素可以是产生适当类型数组的任何东西,而不仅仅是 subARRAY构造。例如:

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,2413}
(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

4 .2.13. 行构造器

行构造函数是一个表达式,该表达式使用其成员字段的值构建行值(也称为复合值)。行构造函数由关键字ROW,左括号,行字段值的零个或多个表达式(用逗号分隔)以及最后一个右括号组成。例如:

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

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

行构造函数可以包含语法* rowvalue * .*,该语法将扩展为行值的元素列表,就像在SELECT列表的顶层使用.*语法时一样(请参见Section 8.16.5)。例如,如果表t具有列f1f2,则它们是相同的:

SELECT ROW(t.*, 42) FROM t;
SELECT ROW(t.f1, t.f2, 42) FROM t;

Note

在 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)

行构造函数可用于构建要存储在复合类型表列中的复合值,或将其传递给接受复合参数的函数。此外,还可以比较两个行的值或使用IS NULLIS NOT NULL测试一行,例如:

SELECT ROW(1,2.5,'this is a test') = ROW(1, 3, 'not the same');

SELECT ROW(table.*) IS NULL FROM table;  -- detect all-null rows

有关更多详细信息,请参见Section 9.23。如Section 9.22所述,行构造函数也可以与子查询结合使用。

4 .2.14. 表达评估规则

未定义子表达式的评估 Sequences。特别是,运算符或函数的 Importing 不必从左到右或以任何其他固定 Sequences 进行评估。

此外,如果只能通过评估表达式的某些部分来确定表达式的结果,则可能根本不会评估其他子表达式。例如,如果一个人写道:

SELECT true OR somefunc();

somefunc()(可能)根本不会被调用。如果一个人写道:

SELECT somefunc() OR true;

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

因此,将带有副作用的函数用作复杂表达式的一部分是不明智的。依靠WHEREHAVING子句中的副作用或评估 Sequences 特别危险,因为这些子句在制定执行计划时会被大量重新处理。这些子句中的布尔表达式(AND/OR/NOT组合)可以按照布尔代数定律允许的任何方式进行重组。

当必须强制执行评估 Sequences 时,可以使用CASE构造(请参见Section 9.17)。例如,这是一种试图避免在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构造将挫败优化尝试,因此仅应在必要时执行。 (在此特定示例中,最好 Rewritey > 1.5*x来避开该问题.)

但是,CASE不能解决所有问题。上面说明的技术的局限性在于它不能阻止对常量子表达式的早期评估。如Section 37.6中所述,标记为IMMUTABLE的函数和运算符可以在计划查询时而不是在执行查询时进行评估。因此,例如

SELECT CASE WHEN x > 0 THEN x ELSE 1/0 END FROM tab;

即使计划表中的每一行都具有x > 0,因此ELSE分支也永远不会在运行时 Importing,但由于计划者试图简化常量子表达式,因此很可能导致除以零的失败。

尽管该特定示例可能看起来很愚蠢,但是在函数内执行的查询中可能会出现明显不涉及常量的相关情况,因为出于规划目的,可以将函数参数和局部变量的值作为常量插入查询中。在 PL/pgSQL 函数中,例如,使用IF-THEN-ELSE语句来保护有风险的计算比将其嵌套在CASE表达式中要安全得多。

同类的另一个限制是CASE不能阻止对其中包含的聚合表达式的求值,因为在考虑SELECT list 或HAVING子句中的其他表达式之前会计算聚合表达式。例如,以下查询尽管看起来已经受到了保护,但可能会导致被零除的错误:

SELECT CASE WHEN min(employees) > 0
            THEN avg(expenses / employees)
       END
    FROM departments;

min()avg()聚合是在所有 Importing 行上同时计算的,因此,如果任何行的employees等于零,那么在没有机会测试min()的结果之前将发生零除错误。而是使用WHEREFILTER子句来防止出现问题的 Importing 行首先到达聚合函数。