当前位置:Gxlcms > 数据库问题 > 4.2. PostgreSQL值表达式

4.2. PostgreSQL值表达式

时间:2021-07-01 10:21:17 帮助过:9人阅读

比如,下面的代码计算 2 的平方根:

sqrt(2)

内置函数的列表在章9里。其它函数可由用户添加。

4.2.7. 聚集表达式

一个聚集表达式代表一个聚集函数对查询选出的行的处理。一个聚集函数把多个输入缩减为一个输出值,比如给输入求和或求平均。一个聚集表达式的语法是下列之一:

aggregate_name (expression [ , ... ] )
aggregate_name (ALL expression [ , ... ] )
aggregate_name (DISTINCT expression [ , ... ] )
aggregate_name ( * )

这里的 aggregate_name 是前面定义的聚集(可能是带有模式的全称),而 expression 是一个本身不包含聚集表达式的任意值表达式。

第一种形式的聚集表达式为所有非 NULL 的输入行调用聚集(实际上,是否忽略 NULL 由聚集函数决定,但是所有标准的聚集函数都忽略它们)。第二种形式与第一种等价(因为 ALL 是缺省值)。第三种形式为所有输入行中所有唯一的非 NULL 值调用聚集。最后一种形式为每个输入行(不管是否为 NULL)调用一次聚集;因为没有声明特定的输入值。通常它只用于 count(*) 聚集函数。

比如,count(*) 生成输入行的总数;count(f1) 生成 f1 不为 NULL 的输入行数;count(distinct f1) 生成 f1 唯一且非 NULL 的行数。

预定义的聚集函数在节9.15里描述。其它聚集函数可以由用户增加。

一个聚集表达式只能在SELECT 命令的结果列表或者 HAVING 子句里出现。禁止在其它子句里出现(比如 WHERE 子句),因为这些子句逻辑上在生成聚集结果之前计算。

如果一个聚集表达式出现在一个子查询里(参阅节4.2.9和节9.16),聚集通常是在子查询中进行计算。但是如果聚集的参数只包含外层查询的变量则例外:这个聚集会属于离他最近的外层查询,并且在该查询上进行计算。该聚集表达式整体上属于它出现的子查询对外层查询的引用,其作用相当于子查询每一次计算中的一个常量。前述限制(聚集表达式只能出现在结果列或者 HAVING 子句中)只适用于聚集所属的查询层。

【注意】PostgreSQL 目前并不支持带有多个输入表达式的 DISTINCT

4.2.8. 类型转换

一个类型转换声明一个从一种数据类型到另外一种数据类型的转换。PostgreSQL 接受两种等效的类型转换语法:

CAST ( expression AS type )
expression::type

CAST 语法遵循 SQL 标准;:: 语法是 PostgreSQL 历史用法。

如果对一个已知类型的值表达式应用转换,它代表一个运行时类型转换。只有在已经定义了合适的类型转换操作的情况下,该转换才能成功。请注意这一点和用于常量的转换略有区别(如节4.1.2.5所示)。一个应用于字符串文本的转换表示给该字符串文本的值赋予一个初始类型,因此它对于任何类型都会成功(如果字符串文本的内容符合该数据类型的输入语法)。

如果一个值表达式的值对某类型而言不存在混淆的情况,那么我们可以省略明确的类型转换(比如,在给一个表字段赋值的时候),而由系统自动执行类型转换。不过,自动转换只适用于那些系统表中标记着"OK to apply implicitly"的转换函数。其它转换函数必须用明确的转换语法调用。这些限制是为了避免一些怪异的转换被自动的应用。

我们也可以用函数风格的语法声明一个类型转换:

typename ( expression )

不过,这个方法只能用于那些类型名同时也是有效函数名的类型。比如,double precision 就不能这么用,但是等效的 float8 可以。同样,interval, time, timestamp 如果加了双引号也只能这么用,因为存在语法冲突。因此,函数风格的类型转换会导致不一致,所以应该避免这么使用。函数样语法实际上就是一个函数调用。如果使用两种标准转换语法做运行时转换,那么它将在内部调用一个已注册的函数执行转换。通常,这种转换函数和它们的输出类型同名,但是可以移植的程序不能依赖这一点。

4.2.9. 标量子查询

一个标量子查询是一个放在圆括弧里只返回一行一列的普通 SELECT 查询(参阅章7获取有关书写查询的信息)。该 SELECT 将被执行,而其返回值将在周围的值表达式中使用。把一个返回超过一行或者超过一列的查询用做标量查询是错误的。不过,子查询不返回行则不算错误(标量结果被认为是 NULL)。子查询可以引用外围查询的变量,这些变量在每次子查询中当做常量使用。参见节9.16 以及http://www.infocool.net以获取其它包含子查询的表达式。

比如,下面的查询找出每个州中的最大人口数量的城市:

SELECT name, (SELECT max(pop) FROM cities WHERE cities.state = states.name)
    FROM states;

4.2.10. 数组构造器

一个数组构造器是一个表达式,它从自身成员元素上构造一个数组值。一个简单的数组构造器由关键字 ARRAY 、一个左方括弧 [ 、一个或多个表示数组元素值的表达式(用逗号分隔)、一个右方括弧 ] 组成。比如

SELECT ARRAY[1,2,3+4];
  array
---------
 {1,2,7}
(1 row)

数组元素类型是成员表达式的公共类型,使用和 UNIONCASE构造一样的规则决定(参阅节10.5)。

多维数组值可以通过嵌套数组构造器的方法来制作。内层构造器中的 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 构造。比如:

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)

我们也可以从一个子查询的结果中构造一个数组。此时,数组构造器是关键字 ARRAY 后跟着一个用圆括弧(不是方括弧)包围的子查询。比如:

SELECT ARRAY(SELECT oid FROM pg_proc WHERE proname LIKE ‘bytea%‘);
                          ?column?
-------------------------------------------------------------
 {2011,1954,1948,1952,1951,1244,1950,2005,1949,1953,2006,31}
(1 row)

子查询必须只返回一个单独的字段。生成的一维数组将为子查询里每行结果生成一个元素,元素类型匹配子查询的输出字段。

ARRAY 建立的数组下标总是从壹开始。有关数组的更多信息,参阅节8.10。

4.2.11. 行构造器

行构造器是一个从提供给它的成员字段数值中构造行值(也叫复合类型值)的表达式。一个行构造器由关键字 ROW 、一个左圆括弧、零个或多个作为行字段值的表达式(用逗号分隔)、一个右圆括弧组成。比如:

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

如果在列表里有多个表达式,那么关键字 ROW 是可选的。

行构造器可以包含 rowvalue.* 语法,它将被扩展为行值元素的列表,就像将 .* 语法用于一个 SELECT 列表顶层一样。例如,如果表 tf1f2 两个字段,那么下面两句是等价的:

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;

-- 因为只有一个getf1()存在,所以不需要类型转换
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;

-- 现在我们需要类型转换以表明调用哪个函数:
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

更多的细节,请参阅节9.17。行构造器还可以用于连接子查询,这些在节9.16里面有详细讨论。

4.2.12. 表达式计算规则

子表达式的计算顺序是没有定义的。特别要指出的是,一个操作符或者函数的输入并不一定是按照从左向右的顺序或者以某种特定的顺序进行计算的。

另外,如果一个表达式的结果可以通过只判断它的一部分就可以得到,那么其它子表达式就可以完全不计算了。比如,如果我们这么写

SELECT true OR somefunc();

那么 somefunc() 就(可能)根本不会被调用。即使像下面这样写也是一样

SELECT somefunc() OR true;

请注意这和某些编程语言里从左向右"短路"是不一样的。

因此,拿有副作用的函数作为复杂表达式的一部分是不明智的。在 WHEREHAVING 子句里依赖副作用或者是计算顺序是特别危险的,因为这些子句都是作为生成一个执行规划的一部分进行了大量的再处理。在这些子句里的布尔表达式(AND/OR/NOT 的组合)可以用布尔代数运算律允许的任何方式进行识别。

如果需要强制计算顺序,那么可以使用 CASE 构造(参阅节9.13)。比如,下面是一种企图避免在 WHERE 子句里被零除的不可靠方法:

SELECT ... WHERE x <> 0 AND y/x > 1.5;

更多信息参考http://www.infocool.net/PostgreSQL/index.htm

但是下面这个是安全的:

SELECT ... WHERE CASE WHEN x <> 0 THEN y/x > 1.5 ELSE false END;

这种风格的 CASE 构造会阻止优化,因此应该只在必要的时候才使用。在这个特殊的例子里,毫无疑问写成 y > 1.5*x 更好。

4.2. PostgreSQL值表达式

标签:

人气教程排行