第四十一章. 扩展的 SQL: 聚集

在 Postgres 里的聚集是用状态值和状态转换函数表达的.也就是说,一个聚集可以定义为一些状态,当一条输入的条目被处理时,这些状态被修改.要定义一个新的聚集函数,我们就要选择一个表示状态值的数据类型,一个状态初始值,一个状态转换函数.该状态转换函数只是一个普通函数,也可以用于聚集的环境之外.

实际上,为了便于从现有的函数里构造聚集,一个聚集可以有一到两个独立的状态值,一到两个用于更新那些状态值的转换函数,和一个最终函数用于从最终状态值中计算实际的聚集结果.

这样我们可能最多要涉及到四种数据类型:输入条目的类型,聚集结果的类型和两个状态值的类型.聚集的用户之能看到输入和结果数据类型.

有些状态转换函数需要获取每个连续的输入以计算下一个状态值,而另外一些忽略声明的输入并简单地更新内部状态.(第二种情况最常用的例子是对一些输入条目运行计数统计.)Postgres 的聚集机制为聚集把 sfunc1 定义为一个传递旧状态值和当前输入值的函数,而 sfunc2 是一个只传递旧状态值的函数.

如果我们定义了一个只使用 sfunc1 的聚集,我们就定义了一个对每条记录字段值运行函数进行计算的函数."Sum" 是这种聚集的一个例子."Sum" 从零开始,然后总是把当前记录的值加到它(运行时的)总和上.例如,如果我们想做一个能对复数类型进行 Sum (求和)运算的聚集,我们只需要那个数据类型的累加函数.聚集的定义是:

CREATE AGGREGATE complex_sum (
    sfunc1 = complex_add,
    basetype = complex,
    stype1 = complex,
    initcond1 = '(0,0)'
);

SELECT complex_sum(a) FROM test_complex;

         +------------+
         |complex_sum |
         +------------+
         |(34,53.9)   |
         +------------+
(实际上,我们会把这个聚集命名为 "sum",并依靠 Postgres 来区分对一个复数列应该施用哪种 sum.)

如果我们只定义 sfunc2,我们就声明了一个计算的运行时数据与每条记录的字段值无关的函数."Count" 是这种聚集的最通常的例子."Count" 从零开始然后每条记录往它的运行时总数加一,不管记录值是多少.这里,我们用内建的 int4inc 函数为我们做这件事.该函数往它的参数上加一.这里,我们使用内建的 int4inc 过程为我们完成这件事.这个过程对它的参数加一.

CREATE AGGREGATE my_count (
    sfunc2 = int4inc, -- add one
    basetype = int4,
    stype2 = int4,
    initcond2 = '0'
);

SELECT my_count(*) as emp_count from EMP;

         +----------+
         |emp_count |
         +----------+
         |5         |
         +----------+
"Average" 是同时需要计算运行时的总和和运行时计数的聚集的例子.当所有记录都处理过后,该聚集的最终结果就是用运行时总和除以运行时计数.我们用 int4pl 和 int4inc 函数和 Postgres 整数除法函数,int4div,来计算平均数.
CREATE AGGREGATE my_average (
    sfunc1 = int4pl,     --  sum
    basetype = int4,
    stype1 = int4,
    sfunc2 = int4inc,    -- count
    stype2 = int4,
    finalfunc = int4div, -- division
    initcond1 = '0',
    initcond2 = '0'
);

SELECT my_average(salary) as emp_average FROM EMP;

         +------------+
         |emp_average |
         +------------+
         |1640        |
         +------------+
更详细的信息请参考PostgreSQL 用户手册里的 CREATE AGGREGATE