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 树索引运算符类的“小于” 或“大于” 策略成员,否则优化将永远不会真正生效。
要能够创建聚合函数,您必须对参数类型、状态类型和返回类型具有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
表示它不修改;其他两个值表示它可能会更改转换状态值。有关更多详细信息,请参见下面的 注释。默认值为 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
不能是 strict。在这种情况下,combinefunc
必须确保正确处理 null 状态,并且返回的状态正确存储在聚合内存上下文中。
serialfunc
其 state_data_type
为 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 标准没有提供用户自定义聚合函数。
如果您在文档中发现任何不正确的内容,与您对特定功能的体验不符,或者需要进一步说明,请使用此表单报告文档问题。