pgsql.cc 提供对 postgresql.org 官网内容的中文翻译,由 Pigsty 团队维护。
PostgreSQL 中的聚合函数是依据 状态值和状态转移函数 来定义的。也就是说,聚合通过一个状态值来工作,并在处理每个 后续输入行时更新该状态值。要定义一个新的聚合函数,需要为状态值 选定一种数据类型、为状态选定一个初始值,并指定一个状态转移 函数。状态转移函数只是普通函数,也可以在聚合的上下文之外使用。 如果聚合的期望结果不同于运行状态值中需要保存的数据,还可以指定 一个最终函数。
因此,除了聚合用户所看到的参数和结果数据类型之外,还存在一种内部状态值数据类型,它可能既不同于参数类型,也不同于结果类型。
如果我们定义一个不使用最终函数的聚合,那么得到的就是一个对每 一行的列值进行逐步计算的聚合。sum 就是这种聚合的 一个示例。sum 从零开始,并始终把当前行的值加到 运行总和中。例如,如果我们希望让 sum 聚合能用于 复数数据类型,那么只需要该数据类型的加法函数。聚合定义如下:
CREATE AGGREGATE complex_sum (
sfunc = complex_add,
basetype = complex,
stype = complex,
initcond = '(0,0)'
);
SELECT complex_sum(a) FROM test_complex;
complex_sum
-------------
(34,53.9)
(实际上,我们只需把聚合命名为 sum,并依靠 PostgreSQL 自己判断对 complex 类型的列应用哪种 sum。)
如果没有非空的输入值,上面 sum 的定义将返回零(初始状态条件)。也许我们希望在这种情形下改为返回 null——SQL 标准期望 sum 表现出那样的行为。要做到这一点,只需省略 initcond 子句,使初始状态条件为 null。通常这意味着 sfunc 需要检查 null 的状态条件输入,但对于 sum 以及 max、min 等一些简单聚合,只需把第一个非空输入值插入状态变量,然后从第二个非空输入值开始应用转换函数就足够了。当初始条件为 null 且转换函数被标记为 “strict”(即不对 null 输入调用)时, PostgreSQL 会自动完成这一工作。
“strict”转移函数的另一项默认行为是,只要遇到空值输入,就会保留先前的状态值不变。因此,空值会被忽略。如果你需要对空值输入采用其他行为,就不要把转移函数声明为 strict,而应在函数代码中自行检测空值输入并完成所需处理。
avg(平均值)是更复杂的聚合示例。它需要两个运行 状态:输入的总和与输入的个数。最终结果由二者相除得到。平均值 通常用两元素数组作为状态值来实现。例如,avg(float8) 的内置实现如下所示:
CREATE AGGREGATE avg (
sfunc = float8_accum,
basetype = float8,
stype = float8[],
finalfunc = float8_avg,
initcond = '{0,0}'
);
聚合函数可以使用多态的状态转移函数或最终函数,这样同一组函数 就能用于实现多个聚合。关于多态函数的解释,请参见 第 33.2.5 节。再进一步,聚合函数自身 也可以指定为具有多态输入类型和状态类型,从而让同一个聚合定义 服务于多种输入数据类型。下面是一个多态聚合的示例:
CREATE AGGREGATE array_accum (
sfunc = array_append,
basetype = anyelement,
stype = anyarray,
initcond = '{}'
);
对于任何给定的聚合调用,其实际状态类型都是一种数组类型,该 数组以实际输入类型为元素类型。
下面是以两种不同的实际数据类型作为参数时的输出:
SELECT attrelid::regclass, array_accum(attname)
FROM pg_attribute
WHERE attnum > 0 AND attrelid = 'pg_user'::regclass
GROUP BY attrelid;
attrelid | array_accum
----------+-----------------------------------------------------------------------------
pg_user | {usename,usesysid,usecreatedb,usesuper,usecatupd,passwd,valuntil,useconfig}
(1 row)
SELECT attrelid::regclass, array_accum(atttypid)
FROM pg_attribute
WHERE attnum > 0 AND attrelid = 'pg_user'::regclass
GROUP BY attrelid;
attrelid | array_accum
----------+------------------------------
pg_user | {19,23,16,16,16,25,702,1009}
(1 row)
更多细节请参见CREATE AGGREGATE命令。
译文有误、术语不当或页面显示问题,请到译文仓库 pgsty/pgdoc 报告译文问题。 英文原文本身的问题,请在当前版本的对应页面向上游反馈;上游不再修订已结束维护的版本。