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 ] ) or the old syntax 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 类型的值被指定并存储在数据库中,但它必须是状态值数据类型常量的有效外部表示形式。如果没有提供它,则状态值初始为空。
如果状态过渡函数被声明为 “严格”,那么它就不能用空输入来调用。对于这样的过渡函数,聚集的执行行为如下。含有任何空输入值的行都会被忽略(不会调用该函数,并保留之前的状态值)。如果初始状态值为空,那么在第一个所有输入值都非空的行上,第一个参数值会取代状态值,并且在后续每个所有输入值都非空的行上都会调用该过渡函数。这对于实现像 max 这样的聚集非常方便。注意,只有当 state_data_type 与第一个 arg_data_type 相同时,才能使用这种行为。当这些类型不同时,你必须提供一个非空的初始条件,或者使用非严格的过渡函数。
如果状态过渡函数不是严格的,那么它会在每个输入行上被无条件调用,并且必须自行处理空输入和空状态值。这使聚集的作者可以完全控制聚集对空值的处理方式。
如果最终函数被声明为 “严格”,那么当结束状态值为空时它将不会被调用,而是自动返回一个空结果。(当然,这只是严格函数的正常行为。)无论如何,最终函数都可以选择返回空值。例如,avg 的最终函数在发现输入行数为零时会返回空。
有时,把最终函数声明为不仅接收状态值、还接收与聚集输入值相对应的额外参数会很有用。这样做的主要原因是:当最终函数是多态的,而状态值的数据类型又不足以确定结果类型时。这些额外的参数总是以 NULL 传递(因此在使用 FINALFUNC_EXTRA 选项时最终函数不能是严格的),但它们仍然是有效的参数。例如,最终函数可以利用 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 类型的值。这个函数也可以被声明为 “严格” 的。在这种情况下,当任一输入状态为空时该函数都不会被调用;另一个状态将被作为正确的结果。
对于 state_data_type 为 internal 的聚集函数,combinefunc 不能是严格的。在这种情况下,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 相同,但它用于从当前聚集状态中移除一个值,而不是向其中加入一个值。逆向过渡函数必须具有与前向状态过渡函数相同的严格性属性。
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 子句的聚集调用,也永远不会使用部分聚合,因为在部分聚集期间无法支持那些语义。
See Section 36.12.
CREATE AGGREGATE 是一个 PostgreSQL 语言扩展。SQL 标准没有提供用户定义的聚集函数。