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 会定义一个新的聚合函数或替换一个已有的定义。一些基本且常用的聚合函数已包含在发行版中;它们在 第 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 类型的值,但它必须是状态值数据类型的常量的有效外部表示。如果未提供,则状态值将从 null 开始。
如果状态转换函数被声明为 “strict”(严格),则不能使用 null 输入调用它。使用这种转换函数时,聚合执行行为如下:带有任何 null 输入值的行将被忽略(函数不会被调用,并且会保留先前的值)。如果初始状态值为 null,则在所有输入值都非 null 的第一行,第一个参数值将替换状态值,并且在每个后续所有输入值都非 null 的行上调用转换函数。这对于实现像 max 这样的聚合非常有用。请注意,只有当 state_data_type 与第一个 arg_data_type 相同时,才可用此行为。当这些类型不同时,您必须提供一个非 null 的初始条件或使用一个非严格的转换函数。
如果状态转换函数不是严格的,则它将在每行输入时无条件调用,并且必须自行处理 null 输入和 null 状态值。这允许聚合作者完全控制聚合对 null 值的处理。
如果最终函数被声明为 “strict”(严格),则当最终状态值为 null 时它不会被调用;而是会自动返回一个 null 结果。(当然,这只是严格函数的正常行为。)无论如何,最终函数可以选择返回 null 值。例如,avg 的最终函数在看到零输入行时返回 null。
有时声明最终函数不仅接受状态值,还接受与聚合输入值对应的额外参数会很有用。这样做的主要原因是,如果最终函数是多态的,并且状态值的数据类型不足以确定结果类型。这些额外参数始终作为 NULL 传递(因此,当使用 FINALFUNC_EXTRA 选项时,最终函数不能是严格的),但尽管如此,它们仍然是有效的参数。例如,最终函数可以使用 get_fn_expr_argtype 来识别当前调用中的实际参数类型。
聚合可以可选地支持 移动聚合模式,如 第 36.12.1 节 中所述。这需要指定 MSFUNC、MINVFUNC 和 MSTYPE 参数,以及可选的 MSSPACE、MFINALFUNC、MFINALFUNC_EXTRA、MFINALFUNC_MODIFY 和 MINITCOND 参数。除了 MINVFUNC 之外,这些参数的工作方式与不带 M 的相应简单聚合参数相同;它们定义了一个包含反向转换函数的聚合的单独实现。
参数列表中的 ORDER BY 语法创建了一种特殊类型的聚合,称为 有序集聚合;或者如果指定了 HYPOTHETICAL,则创建 假设集聚合。这些聚合以依赖于顺序的方式操作一组排序值,因此指定输入排序顺序是调用中的一个重要部分。此外,它们可以具有 直接 参数,这些参数是每个聚合而不是每个输入行仅评估一次的参数。假设集聚合是有序集聚合的一个子类,其中一些直接参数在数量和数据类型上必须与聚合参数列匹配。这允许这些直接参数的值作为额外的 “假设” 行添加到聚合输入行的集合中。
聚合可以可选地支持 部分聚合,如 第 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;
进一步的假设是,聚合忽略 null 输入,并且当且仅当没有非 null 输入时才返回 null 结果。通常,数据类型的 < 运算符是 MIN 的正确排序运算符,而 > 是 MAX 的正确排序运算符。请注意,除非指定的运算符是 B-tree 索引运算符类的 “小于” 或 “大于” 策略成员,否则优化将永远不会真正生效。
要创建聚合函数,您必须对参数类型、状态类型(或类型)和返回类型具有 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”(严格)的。在这种情况下,当任一输入状态为 null 时,函数将不会被调用;另一个状态将作为正确的结果。
对于 state_data_type 为 internal 的聚合函数,combinefunc 不能是严格的。在这种情况下,combinefunc 必须确保正确处理 null 状态,并确保返回的状态正确存储在聚合内存上下文中。
serialfunc状态数据类型为 internal 的聚合函数,只有在具有 serialfunc 函数时才能参与并行聚合。该函数必须将聚合状态序列化为 bytea 值,以便传输到另一个进程。此函数必须接受一个 internal 类型的参数并返回 bytea 类型。还需要一个相应的 deserialfunc。
deserialfunc将先前序列化的聚合状态反序列化回 state_data_type。此函数必须接受 bytea 和 internal 两种类型的参数,并产生 internal 类型的结果。(注意:第二个 internal 参数未使用,但出于类型安全原因而必需。)
initial_condition状态值的初始设置。这必须是一个字符串常量,格式符合 state_data_type 的数据类型。如果未指定,则状态值从 null 开始。
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 子句的聚合调用,它将永远不会被使用,因为这些语义无法在部分聚合期间得到支持。
参见 第 36.12 节。
CREATE AGGREGATE 是 PostgreSQL 的语言扩展。SQL 标准不提供用户定义聚合函数。