CREATE AGGREGATE — 定义一个新的聚合函数
CREATE [ OR REPLACE ] AGGREGATEname( [argmode] [argname]arg_data_type[ , ... ] ) ( SFUNC =sfunc, STYPE =state_data_type[ , SSPACE =state_data_size] [ , FINALFUNC =ffunc] [ , FINALFUNC_EXTRA ] [ , FINALFUNC_MODIFY = { READ_ONLY | SHAREABLE | READ_WRITE } ] [ , COMBINEFUNC =combinefunc] [ , SERIALFUNC =serialfunc] [ , DESERIALFUNC =deserialfunc] [ , INITCOND =initial_condition] [ , MSFUNC =msfunc] [ , MINVFUNC =minvfunc] [ , MSTYPE =mstate_data_type] [ , MSSPACE =mstate_data_size] [ , MFINALFUNC =mffunc] [ , MFINALFUNC_EXTRA ] [ , MFINALFUNC_MODIFY = { READ_ONLY | SHAREABLE | READ_WRITE } ] [ , MINITCOND =minitial_condition] [ , SORTOP =sort_operator] [ , PARALLEL = { SAFE | RESTRICTED | UNSAFE } ] ) CREATE [ OR REPLACE ] AGGREGATEname( [ [argmode] [argname]arg_data_type[ , ... ] ] ORDER BY [argmode] [argname]arg_data_type[ , ... ] ) ( SFUNC =sfunc, STYPE =state_data_type[ , SSPACE =state_data_size] [ , FINALFUNC =ffunc] [ , FINALFUNC_EXTRA ] [ , FINALFUNC_MODIFY = { READ_ONLY | SHAREABLE | READ_WRITE } ] [ , INITCOND =initial_condition] [ , PARALLEL = { SAFE | RESTRICTED | UNSAFE } ] [ , HYPOTHETICAL ] ) 或者旧的语法 CREATE [ OR REPLACE ] AGGREGATEname( BASETYPE =base_type, SFUNC =sfunc, STYPE =state_data_type[ , SSPACE =state_data_size] [ , FINALFUNC =ffunc] [ , FINALFUNC_EXTRA ] [ , FINALFUNC_MODIFY = { READ_ONLY | SHAREABLE | READ_WRITE } ] [ , COMBINEFUNC =combinefunc] [ , SERIALFUNC =serialfunc] [ , DESERIALFUNC =deserialfunc] [ , INITCOND =initial_condition] [ , MSFUNC =msfunc] [ , MINVFUNC =minvfunc] [ , MSTYPE =mstate_data_type] [ , MSSPACE =mstate_data_size] [ , MFINALFUNC =mffunc] [ , MFINALFUNC_EXTRA ] [ , MFINALFUNC_MODIFY = { READ_ONLY | SHAREABLE | READ_WRITE } ] [ , MINITCOND =minitial_condition] [ , SORTOP =sort_operator] )
CREATE AGGREGATE定义一个新的聚合函数。 CREATE OR REPLACE AGGREGATE要么定义一个新的聚合函数, 要么替换现有定义。发行版中包含一些基本且常用的聚合函数;其文档见 Section 9.21。如果定义了新类型,或者需要尚未 提供的聚合函数,则可以使用CREATE AGGREGATE提供所需 特性。
在替换现有定义时,不得更改参数类型、结果类型和直接参数的数量。此外, 新定义必须与旧定义属于同一种类(普通聚合、有序集聚合或假想集聚合)。
如果给出了一个模式名(例如CREATE AGGREGATE myschema.myagg ...),则该聚合函数会在指定模式中创建。否则 会在当前模式中创建。
聚合函数由其名称和输入数据类型来标识。如果同一模式中的两个聚合作用于不 同的输入类型,它们可以具有相同的名称。聚合的名称和输入数据类型还必须不 同于同一模式中每个普通函数的名称和输入数据类型。这种行为与普通函数名的 重载完全相同(见CREATE FUNCTION)。
简单聚合函数由一个或两个普通函数构成:一个状态转换函数 sfunc,以及一个可选的最终 计算函数ffunc。其用法如下:
sfunc( internal-state, next-data-values ) ---> next-internal-stateffunc( internal-state ) ---> aggregate-value
PostgreSQL会创建一个数据类型为 stype的临时变量,用来保存 聚合的当前内部状态。对于每个输入行,都会先计算聚合参数值,然后以当前状 态值和新的参数值调用状态转换函数,从而计算新的内部状态值。等到所有行都 处理完毕后,再调用一次最终函数来计算聚合的返回值。如果没有最终函数,则 原样返回结束时的状态值。
聚合函数可以提供一个初始条件,即内部状态值的初始值。它在数据库中以 text类型的值指定和存储,但它必须是该状态值数据类型常量的 合法外部表示。如果未提供,则状态值初始为空值。
如果状态转换函数被声明为“strict”,则不能用空输入调用它。 采用这种转换函数时,聚合执行的行为如下。任何输入值为空的行都会被忽略 (不会调用该函数,并保留先前的状态值)。如果初始状态值为空,则在第一个 所有输入值都非空的行上,第一个参数值会取代状态值,并在之后每一个所有输 入值都非空的行上调用状态转换函数。这对于实现max之 类的聚合很方便。注意,只有当 state_data_type 与第一个 arg_data_type相同时,这种行为 才可用。当这两种类型不同时,必须提供非空初始条件,或者使用非 strict 状态转换函数。
如果状态转换函数不是 strict,则它会无条件地在每个输入行上被调用,并且 必须自行处理空输入和空状态值。这使聚合作者能够完全控制聚合对空值的处理 方式。
如果最终函数被声明为“strict”,那么当结束状态值为空时不会调 用它;而是自动返回空结果。(这当然只是 strict 函数的正常行为。)无论如何, 最终函数都可以选择返回空值。例如,avg的最终函数在发 现输入行数为零时会返回空值。
有时将最终函数声明为不仅接收状态值,还接收与聚合输入值相对应的额外参数 会很有用。这样做的主要原因是,如果最终函数是多态的,状态值的数据类型不 足以确定结果类型。这些额外参数总是以 NULL 传递(因此在使用 FINALFUNC_EXTRA选项时,最终函数不能声明为 strict),但它 们仍然是有效参数。例如,最终函数可以利用 get_fn_expr_argtype来识别当前调用中的实际参数类型。
聚合可以选择支持Section 36.12.1中所述的 移动聚合模式。这要求指定 MSFUNC、MINVFUNC和 MSTYPE 参数,并可选指定 MSSPACE、MFINALFUNC、 MFINALFUNC_EXTRA、MFINALFUNC_MODIFY 和MINITCOND 参数。除MINVFUNC外, 这些参数的工作方式与对应的不带M的简单聚合参数相同; 它们定义了该聚合的一种包含逆向转换函数的独立实现。
参数列表中带有ORDER BY的语法会创建一种称为 有序集聚合的特殊聚合类型;如果指定了 HYPOTHETICAL,则会创建 假想集聚合。这些聚合以依赖顺序的方式对一组已 排序的值进行操作,因此指定输入排序顺序是调用中必不可少的一部分。它们 还可以有直接参数,即每次聚合只求值一次而不是对 每个输入行求值一次的参数。假想集聚合是有序集聚合的一个子类,其中要求 某些直接参数在数量和数据类型上与被聚合的参数列匹配。这使得这些直接参 数的值可以作为一条额外的“假想”行添加到聚合输入行的集合中。
聚合还可以选择支持Section 36.12.4中所述的 部分聚合。这要求指定 COMBINEFUNC参数。如果 state_data_type是 internal,通常还应提供SERIALFUNC和 DESERIALFUNC参数,以便能够进行并行聚合。注意,要启 用并行聚合,该聚合还必须被标记为PARALLEL SAFE。
行为类似于MIN或MAX的聚合,有 时可以通过查阅索引而不是扫描每个输入行来优化。如果该聚合可以这样优化, 请通过指定一个排序操作符来表明。基本要求是,该 聚合必须返回该操作符所诱导的排序顺序中的第一个元素;换句话说:
SELECT agg(col) FROM tab;
必须等价于:
SELECT col FROM tab ORDER BY col USING sortop LIMIT 1;
进一步的假设是,该聚合忽略空输入,并且当且仅当不存在非空输入时返回空 结果。通常,某种数据类型的<操作符是 MIN的合适排序操作符,而>是 MAX的合适排序操作符。注意,除非指定的操作符是 B-树索引操作符类中“小于”或“大于”策略成员,否 则这种优化实际上永远不会生效。
要能够创建聚合函数,你必须在参数类型、状态类型和返回类型上拥有 USAGE权限,并在支持函数上拥有 EXECUTE权限。
name #要创建的聚合函数的名称(可带模式限定)。
argmode #参数的模式:IN或VARIADIC。 (聚合函数不支持OUT参数。)如果省略,默认为 IN。只有最后一个参数可以标记为 VARIADIC。
argname #参数的名称。目前这仅对文档有用。如果省略,则该参数没有名称。
arg_data_type #该聚合函数所操作的输入数据类型。要创建零参数聚合函数,在参数说明列表 的位置写*。(此类聚合的一个示例是 count(*)。)
base_type #在CREATE AGGREGATE的旧语法中,输入数据类型通过 basetype参数指定,而不是写在聚合名称旁边。注意, 这种语法只允许一个输入参数。要用这种语法定义零参数聚合函数,应将 basetype指定为"ANY"(不是 *)。有序集聚合不能用旧语法定义。
sfunc #对每个输入行调用的状态转换函数名称。对于普通的 N 参数聚合函数, sfunc必须接收 N+1 个参数,第一个参数类 型为state_data_type, 其余参数与该聚合声明的输入数据类型匹配。该函数必须返回 state_data_type类型的 值。它接收当前状态值和当前输入数据值,并返回下一个状态值。
对于有序集(包括假想集)聚合,状态转换函数只接收当前状态值和聚合参数, 不接收直接参数。除此之外它与普通情况相同。
state_data_type #聚合状态值的数据类型。
state_data_size #聚合状态值的近似平均大小(以字节为单位)。如果省略该参数或其值为零, 则会基于state_data_type使用默认估计值。 规划器使用这个值来估计分组聚合查询所需的内存。
ffunc #在遍历完所有输入行后调用、用于计算聚合结果的最终函数名称。对于普通聚 集,该函数必须只接收一个类型为state_data_type的参数。聚合的返回 数据类型定义为该函数的返回类型。如果未指定 ffunc,则结束状态值会 被用作聚合结果,返回类型也就是state_data_type。
对于有序集(包括假想集)聚合,最终函数不仅接收最终状态值,还会接收所 有直接参数的值。
如果指定了FINALFUNC_EXTRA,则除了最终状态值和任何 直接参数之外,最终函数还会接收额外的 NULL 值,它们对应于该聚合的常规 (被聚合的)参数。这主要用于在定义多态聚合时能够正确解析聚合的结果类 型。
FINALFUNC_MODIFY = { READ_ONLY | SHAREABLE | READ_WRITE } #此选项指定最终函数是否为不修改其参数的纯函数。 READ_ONLY表示不会修改;另外两个值表示它可能会更改 状态值。更多细节见下文Notes。 默认值是READ_ONLY,但对于有序集聚合,默认值是 READ_WRITE。
combinefunc #可以选择指定combinefunc 函数,以使聚合函数支持部分聚合。如果提供了它,则 combinefunc必须把两个 state_data_type值组合起 来;这两个值各自包含对某个输入值子集聚合得到的结果,并产生一个新的 state_data_type,表示同 时对这两组输入进行聚合的结果。可以把这个函数看作一种 sfunc:不同之处在于,它 不是处理单个输入行并把它加入运行聚合状态,而是把另一个聚合状态加入运 行状态。
combinefunc必须声明为接 收两个state_data_type 参数,并返回一个state_data_type 值。该函数也可以选择声明为“strict”。在这种情况下,只要 任一输入状态为空,就不会调用该函数;另一个状态会被视为正确结果。
对于state_data_type为 internal的聚合函数, combinefunc不能声明为 strict。在这种情况下, combinefunc必须确保正 确处理空状态,并且返回的状态被正确存储在聚合内存上下文中。
serialfunc #state_data_type为 internal的聚合函数,只有在具备一个 serialfunc函数时才能参与 并行聚合;该函数必须把聚合状态序列化为一个bytea值,以传 输给另一个进程。该函数必须接收一个internal类型参数并返回 bytea类型结果。还需要相应的 deserialfunc。
deserialfunc #将先前序列化的聚合状态反序列化回 state_data_type。该函数 必须接收bytea和internal两个参数,并产生一 个internal类型的结果。(注意:第二个internal 参数未被使用,但出于类型安全的原因必须存在。)
initial_condition #状态值的初始设置。这必须是一个字符串常量,其形式必须能被数据类型 state_data_type接受。 如果未指定,状态值初始为空值。
msfunc #在移动聚合模式下,对每个输入行调用的前向状态转换函数名称。它与常规转 换函数完全相同,只是其第一个参数和结果都是 mstate_data_type类型,这可能与 state_data_type不同。
minvfunc #在移动聚合模式中使用的逆向状态转换函数名称。该函数的参数和结果类型与 msfunc相同,但它不是把值添加到当前聚合状 态中,而是从中移除一个值。逆向转换函数必须与前向状态转换函数具有相同 的 strict 属性。
mstate_data_type #使用移动聚合模式时,聚合状态值的数据类型。
mstate_data_size #使用移动聚合模式时,聚合状态值的近似平均大小(以字节为单位)。其作用 与state_data_size相同。
mffunc #在使用移动聚合模式时,在遍历完所有输入行后调用、用于计算聚合结果的最 终函数名称。其工作方式与ffunc相同,只是其第一 个参数的类型是mstate_data_type,而额外的哑 参数通过写MFINALFUNC_EXTRA来指定。由 mffunc或 mstate_data_type决定的聚合结果类型,必须 与该聚合常规实现决定的结果类型一致。
MFINALFUNC_MODIFY = { READ_ONLY | SHAREABLE | READ_WRITE } #此选项类似于FINALFUNC_MODIFY,只是它描述了移动聚 集最终函数的行为。
minitial_condition #使用移动聚合模式时,状态值的初始设置。其作用与 initial_condition相同。
sort_operator #与类MIN或类MAX聚合关联的排 序操作符。这只是一个操作符名称(可带模式限定)。假定该操作符与该聚合 具有相同的输入数据类型(该聚合必须是单参数普通聚合)。
PARALLEL = { SAFE | RESTRICTED | UNSAFE } #PARALLEL SAFE、PARALLEL RESTRICTED和PARALLEL UNSAFE的含义与 CREATE FUNCTION中的含义相同。如果一个聚合被标记为 PARALLEL UNSAFE(这正是默认值!)或者 PARALLEL RESTRICTED,就不会考虑将它并行化。注意, 规划器不会查阅聚合支持函数的并行安全标记,它只看聚合本身的标记。
HYPOTHETICAL #仅用于有序集聚合。该标志指定应按假想集聚合的要求处理聚合参数:也就是, 最后几个直接参数必须与聚合(WITHIN GROUP)参数的 数据类型匹配。HYPOTHETICAL标志不会影响运行时行为, 只影响在解析时对聚合参数的数据类型和排序规则的确定。
CREATE AGGREGATE的参数可以按任意顺序书写,而不只 是按上面展示的顺序。
在指定支持函数名的参数中,如果需要可以写模式名,例如 SFUNC = public.sum。但是不要在那里写参数类型 — 支持函数的参数类型由其他参数决定。
通常,PostgreSQL 函数应当是真正不修改其输入值的函数。然而,聚合的状 态转换函数在聚合上下文中使用时,允许取巧地就地修 改其状态值参数。与每次都新建一份状态值副本相比,这样可能带来显著 的性能收益。
同样,虽然通常也期望聚合最终函数不修改其输入值,但有时无法避免修改 状态值参数。这种行为必须通过FINALFUNC_MODIFY参数 声明。READ_WRITE值表示最终函数会以未指定的方式修 改状态值。这个值会阻止将该聚合用作窗口函数,也会阻止对共享相同输入值 和状态转换函数的聚合调用合并状态值。SHAREABLE值表 示在调用最终函数之后,不能再应用状态转换函数,但可以对结束时的状态值 执行多次最终函数调用。这个值同样会阻止将该聚合用作窗口函数,但允许合 并状态值。(也就是说,这里关注的优化不是重复应用同一个最终函数,而是 把不同的最终函数应用到同一个结束状态值上。只要这些最终函数都 没有标记为READ_WRITE,这就是允许的。)
如果一个聚合支持移动聚合模式,那么当它被用作具有移动帧起点的窗口的窗 口函数时(即帧起点模式不是UNBOUNDED PRECEDING), 就能提高计算效率。从概念上讲,当前向转换函数在输入值从底部进入窗口帧 时把它们加入聚合状态,逆向转换函数则在这些值从顶部离开窗口帧时再次将 其移除。因此,移除值时,总是按它们被加入时的相同顺序移除。每当调用逆 向转换函数时,它收到的就是最早被加入但尚未被移除的参数值。逆向转换函 数可以假定,在它移除最旧的那一行之后,当前状态中至少还会保留一行。(若 非如此,窗口函数机制就会简单地重新开始一次全新的聚合,而不是使用逆向 转换函数。)
用于移动聚合模式的前向转换函数不允许返回 NULL 作为新的状态值。如果逆 向转换函数返回 NULL,就表示该逆向函数无法针对这个特定输入逆转状态计 算,因此会从当前帧起始位置开始重新计算聚合。这一约定使得移动聚合模式 可用于这样一些场景:在少数不常见的情况下,难以从运行状态值中逆向消除 对应输入的影响。
如果没有提供移动聚合实现,聚合仍然可以与移动帧一起使用,但每当帧起点 移动时,PostgreSQL都会重新计算整个聚合。 注意,无论聚合是否支持移动聚合模式,PostgreSQL 都能在不重新计算的情况下处理移动的帧结束位置;做法是继续把新值添加到 聚合状态中。这就是为什么将聚合用作窗口函数时要求最终函数必须是只读的: 它不能损坏聚合的状态值,这样即使已经针对一组帧边界得到了聚合结果值, 聚合也能继续进行。
有序集聚合的语法允许对最后一个直接参数和最后一个聚合 (WITHIN GROUP)参数都指定 VARIADIC。但是,当前实现从两个方面限制了 VARIADIC的使用。第一,有序集聚合只能使用 VARIADIC "any",不能使用其他可变参数数组类型。第 二,如果最后一个直接参数是VARIADIC "any",则只能有 一个聚合参数,并且它也必须是VARIADIC "any"。 (在系统目录使用的表示中,这两个参数会合并为单个 VARIADIC "any"项,因为pg_proc 无法表示具有多个VARIADIC参数的函数。)如果该聚合是 假想集聚合,则与VARIADIC "any"参数匹配的直接参数就 是假想参数;其前面的任何参数都表示额外的直接参数,它们不受必须与聚合 参数匹配的约束。
当前,有序集聚合无需支持移动聚合模式,因为它们不能被用作窗口函数。
部分(包括并行)聚合当前不支持有序集聚合。此外,对于包含 DISTINCT或ORDER BY子句的聚合调 用,也永远不会使用部分聚合,因为在部分聚合期间无法支持这些语义。
CREATE AGGREGATE是 PostgreSQL的语言扩展。SQL 标准不提供用户 定义聚合函数。
如果您发现文档中有不正确的内容、与您使用特定功能的经验不符或需要进一步说明,请使用此表单来报告文档问题。