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 开始。
如果状态转换函数声明为 “严格”,那么它就不能用空值输入来调用。使用这样的转换函数,聚合执行表现如下。任何空值输入的行都将被忽略(不会调用该函数并且会保留前一个状态值)。如果初始状态值为空值,那么第一行为所有非空值输入值,第一个参数值将替换状态值,而且该转换函数将在后续每一行为所有非空值输入值时被调用。这对于实现诸如 max
的聚合十分方便。请注意,只有当 state_data_type
与第一个 arg_data_type
相同时才可以使用这种行为。当这些类型不同时,你必须提供一个非空值初始条件或使用一个非严格转换函数。
如果状态转换函数不是严格的,那么它将在每一个输入行上无条件地被调用,并且自身必须处理空值输入和空值状态。这允许聚合作者完全控制聚合对空值的处理方式。
如果最终函数声明为 “严格”,那么当结束状态值为空值时,它将不会被调用,而是自动返回一个空值结果。(当然,这只是严格函数的正常行为。)无论如何,最终函数都有权返回一个空值。例如,当 avg
的最终函数看到有零个输入行时,它返回空值。
有时声明最终函数不仅获取状态值,而且还获取对应于聚合输入值的额外参数是有用的。执行此操作的主要原因是如果最终函数是多态的,则状态值的数据类型不适合固定结果类型。这些额外参数始终作为 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;
进一步的假设是聚合忽略空输入,并且仅当没有非空输入时才提供空结果。通常,数据类型的 <
运算符是 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
,则除最终状态值和任何直接参数外,最终函数还会接收与聚集函数常规(聚集)参数相对应的 extra NULL 值。这主要用于在定义多态聚集函数时允许正确解析聚集函数结果类型。
FINALFUNC_MODIFY
= { READ_ONLY
| SHAREABLE
| READ_WRITE
}此选项指定最终函数是否是不会修改其参数的纯函数。 READ_ONLY
表示不会修改;其它两个值表示它可能会更改转换状态值。有关更多详细信息,请参见以下的 注释。对于有序集合聚合(默认为 READ_WRITE
)之外的聚合,默认为 READ_ONLY
。
combinefunc
可以根据需要指定 combinefunc
函数以允许聚合函数支持部分聚合。如果提供,则 combinefunc
必须合并两个 state_data_type
值,每个值都包含对某些输入值子集进行聚合的结果,以生成一个新的 state_data_type
,表示对两组输入进行聚合的结果。可以将此函数视为 sfunc
,不同之处在于,它不会对各个输入行执行操作并将其添加到正在运行的聚合状态,而是向正在运行的状态中添加另一个聚合状态。
必须将 combinefunc
声明为采用两个 state_data_type
参数并返回一个 state_data_type
值。此函数可以选择表示为 “严格”。在这种情况下,当任何一个输入状态为 null 时,系统不会调用该函数;另一个状态将被视为正确结果。
对于其 state_data_type
为 internal
的聚合函数, combinefunc
不能是严格的。在这种情况下, combinefunc
必须确保正确处理 null 状态,并且正确存储要返回的状态到聚合内存上下文中。
serialfunc
仅当聚合函数具有 serialfunc
函数(该函数必须将聚合状态序列化到 bytea
值以传输到另一个进程)时,其 state_data_type
为 internal
的聚合函数才能参与并行聚合。此函数必须采用一个 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 函数被认为是不会修改其输入值 True 函数。但聚合转换函数在聚合上下文中使用时,可以欺骗并就地修改其转换状态参数。与每次生成转换状态的新副本相比,它可以提供显著的性能优势。
类似地,虽然通常情况下,在聚合最终函数中并不期望修改输入值,但有时避免修改转换状态参数则是不切实际的。必须使用 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 标准没有提供用户定义的聚合函数。