值表达式用在各种语法环境中,比如在 SELECT 命令的目标列表中,在 INSERT 或 UPDATE 中用做新的列值,或者在许多命令中的 搜索条件中使用. 我们有时候把值表达式的结果叫做标量, 以便与一个表表达式的结果相区别(是一个表).因此值表达式也叫做 标量表达式(或者更简单的 表达式).表达式语法允许对来自基本部分的 数值进行算术,逻辑,集合,和其它操作的运算.
值表达式是下列内容之一:
一个常量或者文本值;参阅 Section 1.1.2.
一个字段引用.
一个位置参数引用,在函数声明体中.
一个操作符调用.
一个函数调用.
一个聚集表达式.
一个类型转换.
一个标量子查询.
另外一个在圆括弧里面的值表达式,可以用于子表达式分组和覆盖优先级.
除了这个列表以外,还有许多构造可以归类为表达式,但是不遵循任何 通用的语法规则. 它们通常有函数或操作符的语义,并且在 Chapter 6 里合适的位置描述. 一个例子是 IS NULL 子句.
我们已经在 Section 1.1.2 里有讨论过的 内容了.下面的节讨论剩下的选项.
一个字段可以用下面形式的引用:
correlation.columnname
或者
correlation.columnname[subscript]
(这里,方括弧 [ ] 意思是按文本出现。)
correlation 是一个表的名字(可能是全称), 或者是用FROM子句这样的方法定义的表的别名,或者是关键字 NEW 或 OLD. (NEW和 OLD只能出现在一条改写规则中, 而其他相关的名字可以用于任意 SQL 语句中.) 如果在当前查询中所使用的所有表中,该字段名字是唯一的, 那么这个相关名字和分隔用的点就可以省略.
如果 column 是一个数组类型,那么可选的 subscript 选择该数组中指定的一个或多个元素. 如果没有提供脚标,那么选出整个数组.(请参考 Section 5.12 获取关于数组的更多信息.)
位置参数引用用于标识从外部给一个 SQL 语句的一个参数. 参数用于 SQL 函数定义语句和准备的查询.一个参数的形式如下:
$number
比如,看看一个函数 dept 的定义, 如下
CREATE FUNCTION dept(text) RETURNS dept AS 'SELECT * FROM dept WHERE name = $1' LANGUAGE SQL;
在函数被调用的时候这里的 $1 将被第一个函数的参数代替.
操作符调用有三种语法∶
expression operator expression (双目中缀操作符) |
operator expression (单目前缀操作符) |
expression operator (单目后缀操作符) |
OPERATOR(schema.operatorname)
具体存在哪个操作符以及它们是单目还是双目取决于系统或用户 定义了什么操作符.Chapter 6 描述了内建的操作符.
函数调用的语法是合法函数名字(可能有模式名修饰), 后面跟着在圆括弧里的它的参数列表:
function ([expression [, expression ... ]] )
比如,下面的代码计算 2 的平方根:
sqrt(2)
内建函数的列表在 Chapter 6 里. 其它函数可以由用户添加.
一个聚集表达式代表一个聚集函数对 一个查询选出的行的处理.一个聚集函数把多个输入缩减为一个输出值, 比如给输入求和或平均.一个聚集表达式的语法是下列之一:
aggregate_name (expression) aggregate_name (ALL expression) aggregate_name (DISTINCT expression) aggregate_name ( * )
这里 aggregate_name 是前面定义的聚集, (可能是全称), 而 expression 是一个本身不包含聚集表达式 的任意值表达式.
第一种形式的聚集表达式为所有表达式生成非空值的输入行调用聚集. (实际上,是否忽略空值由聚集函数决定 --- 但是所有标准的聚集 函数都忽略它们.) 第二种形式和第一种一样,因为 ALL 是缺省值.第三种形式为所有输入行里找到 表达式的所有唯一的非空值调用聚集. 最后一种形式为每个输入行(不管是空还是非空)调用一次聚集; 因为没有声明特定的输入值.通常它只是对 count() 聚集函数有用.
比如,count(*) 生成输入行的总数; count(f1) 生成 f1 为非空的 输入行数;count(distinct f1) 生成 f1 唯一非空的行数.
预定义的聚集函数在 Section 6.14 里描述. 其它聚集函数可以由用户增加.
一个类型转换声明一个从一种数据类型到另外一种数据类型的转换. PostgreSQL 接受两种等效的类型转换语法∶
CAST ( expression AS type ) expression::type
CAST 语法遵循 SQL;:: 的语法是 PostgreSQL 传统用法.
如果对一个已知类型的值表达式应用转换,它代表一个运行时类型转换. 只有在存在合适的类型转换函数的情况下,该转换才能成功. 请注意这一点和用于常量的转换略有区别,如 Section 1.1.2.4 所示. 一个应用于某个未修饰的字串文本的转换表示给一个字串文本数值赋予一个 初始化类型,因此它对于任何类型都会成功(如果字串文本的内容符合 该数据类型的输入语法接受.)
如果对于一个值表达式生成的数值对某类型而言不存在混淆的情况, 那么我们可以省略明确的类型转换(比如,在给一个表字段赋值的时候); 在这样的情况下,系统将自动附加一个类型转换. 不过,自动转换只适用于那些系统表中标记着 "OK to apply implicitly" 的转换函数. 其它转换函数必须用明确的转换语法调用. 这些限制是为了避免一些怪异的转换被应用.
我们也可以用函数样的语法声明一个类型转换∶
typename ( expression )
不过,这个方法只能用于那些名字同时也是有效函数名字的类型. 比如,double precision 就不能这么用, 但是等效的 float8 可以.同样,interval, time,和 timestamp 如果加了双引号也只能 这么用,因为存在语法冲突.因此,函数样的类型转换会导致不一致, 所以可能应该避免在新应用中这么用. (函数样语法实际上就似乎一个函数调用。如果使用两种标准转换语法 做运行时转换,那么它将在内部调用一个已注册得函数执行转换。通常, 这种转换函数和它们得输出类型同名,但是这个要点可不是那些可以移植 的程序可以依赖的东西。)
一个标量子查询是一个放在圆括弧里的普通 SELECT查询, 它只返回只有一个字段的一行.(参阅 Chapter 4 获取有关写查询的信息。) 该 SELECT 将被执行, 而其单个返回值将在周围的值表达式中使用. 把一个返回超过一行或者超过一列的查询用做标量查询是错误的. (不过,在特定的执行中,子查询不返回行则不算错误;标量结果认为是 NULL.)该子查询可以引用周围查询的变量, 那些变量也是在计算任意子查询的时候当做常量使用的. 又见 Section 6.15.
比如,下面的查询找出每个州中的最大人口数量的城市∶
SELECT name, (SELECT max(pop) FROM cities WHERE cities.state = states.name) FROM states;
子表达式的计算顺序是没有定义的。特别要指出的是,一个操作符或者 函数的输入并不一定是按照从左向右的顺序或者以某种特定的顺序进行 计算的。
另外,如果一个表达式的结果可以通过只判断它的一部分就可以得到, 那么其它子表达式就可以完全不计算了。比如,如果我们这么写
SELECT true OR somefunc();
那么 somefunc() 就(可能)根本不会被调用。 如果我们写下面的,也可能会是这样
SELECT somefunc() OR true;
请注意这里和某些编程语言里的从左向右"短路"是不一样的。
因此,拿那些有副作用的函数作为复杂表达式的一部分是不明智的选择。 在 WHERE 和 HAVING 子句里面依赖副作用或者是 计算顺序是特别危险的,因为这些子句都是作为生成一个执行规划的一部分 进行了大量的再处理。在这些子句里的布尔表达式(AND/OR/NOT 的组合)可以以布尔代数运算律允许的任意方式进行识别。
如果强制计算顺序非常重要,那么可以使用 CASE 构造(参阅 Section 6.12)。 比如,下面是一种视图避免在 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 构造会阻止优化,因此应该只在必要的时候使用。