【问题标题】:Trying to create aggregate function in PostgreSQL试图在 PostgreSQL 中创建聚合函数
【发布时间】:2012-09-06 05:51:57
【问题描述】:

我正在尝试在 PostgreSQL 中创建新的聚合函数来代替 sum() 函数

我从手册here开始我的旅程。

因为我想创建一个函数,它接受一个双精度值数组,对它们求和,然后进行一些额外的计算,所以我首先创建了最终函数:

takes double precision as input and gives double precision as output    
DECLARE
      v double precision;
BEGIN
      IF tax > 256 THEN
            v := 256;
      ELSE
            v := tax;
      END IF;
      RETURN v*0.21/0.79;
END;

然后我想创建一个聚合函数,它接受一个双精度值数组并输出一个双精度值供我之前的函数处理。

CREATE AGGREGATE aggregate_ee_income_tax (float8[]) (
  sfunc = array_agg
 ,stype = float8
 ,initcond = '{}'
 ,finalfunc = eeincometax);

当我运行该命令时,我得到的是:

错误:函数array_agg(双精度,双精度[])确实 不存在

我有点卡在这里,因为手册将array_agg() 列为现有函数。我做错了什么?

另外,当我跑步时:

\da
                     List of aggregate functions
 Schema | Name | Result data type | Argument data types | Description 
--------+------+------------------+---------------------+-------------
(0 rows)

我的安装根本没有聚合函数?还是只列出用户定义的函数?

基本上我想了解的内容:

1) 我可以使用现有函数来汇总我的数组值吗?

2) 我如何知道函数的输入和输出数据类型?文档声称array_agg() 接受任何类型的输入。

3) 我自己的聚合函数有什么问题?

编辑 1

为了提供更多信息和更清晰地了解我正在努力实现的目标:

我对几个表有一个巨大的查询,如下所示:

SELECT sum(tax) ... from (SUBQUERY) as foo group by id

我想用我自己的聚合函数替换那个 sum 函数,这样我就不必在后端进行额外的计算 - 因为它们都可以在数据库级别完成。

编辑 2

接受蚂蚁的回答。由于最终解决方案来自 cmets,因此我将其发布在这里以供参考:

CREATE AGGREGATE aggregate_ee_income_tax (float8)
(
 sfunc = float8pl
,stype = float8
,initcond = '0.0'
,finalfunc = eeincometax
);

【问题讨论】:

  • 从你的描述我不明白为什么你不能只使用yourfinalfunc(sum(tax))来转换最终结果?这将完全避免创建聚合函数。
  • 因为我提到的那个查询是以编程方式放在一起的,如果我要在另一个函数之上添加一个函数,那么我将不得不在将查询放在一起的过程中进行很多更改。添加一堆条件和诸如此类的东西 - 所有这些都会使以后更难理解代码。因此,只需更改 sum 函数就更容易了。

标签: postgresql aggregate-functions plpgsql


【解决方案1】:

Array agg 是一个聚合函数而不是常规函数,因此不能用作新聚合的状态转换函数。您要做的是创建一个聚合函数,该函数具有与 array_agg 相同的状态转换函数和自定义最终函数。

不幸的是,array_agg 的状态转换函数是根据内部数据类型定义的,因此无法重用。幸运的是,核心中有一个现有功能已经可以满足您的需求。

CREATE AGGREGATE aggregate_ee_income_tax (float8)(
    sfunc = array_append,
    stype = float8[],
    initcond = '{}',
    finalfunc = eeincometax);

还请注意,您的类型混淆了,您可能希望将一组浮点数聚合到一个数组,而不是将一组数组聚合到一个浮点数。

【讨论】:

  • 太棒了。这让我更接近我想要的,但 eeincometax finalfunc 期望单个 float8 值而不是数组。并且 array_append 似乎只输出数组。所以我仍然需要在我的 finalfunction 中进行实际的求和,或者是否有核心函数输出单个值?
  • 所以你想要一个函数作为状态转换,它接受两个浮点数并返回它们的总和。巧合的是,这就是 + 运算符的作用。为 float8 实现 + 运算符的函数称为 float8pl。将 sfunc 更改为 float8pl 并将 stype 更改为 float8 ,您应该是金色的。
  • 不,我想要聚合函数,它需要一堆记录并对它们求和,然后将这个总和作为输入提供给我的 finalfunc。就像我说的 - 我希望函数可以在我的查询中替换 sum() 函数。
  • 是的,这就是它的作用。 float8pl 作为转换函数将值相加并将最终值传递给您的函数。另外我忘了提到您需要将 initcond 更改为 0.0。
  • 太棒了。至少我通过了一切没有错误。但价值输出似乎是错误的。我现在正在调试:)。
【解决方案2】:

除了@Ants 的优秀建议:

1.) 您的最终功能可以简化为:

CREATE FUNCTION eeincometax(float8)
  RETURNS float8 LANGUAGE SQL AS
$func$
SELECT (least($1, 256) * 21) / 79
$func$;

2.) 你好像在处理金钱问题?在这种情况下,我强烈建议使用 numeric(首选)或 money 类型。浮点运算通常不够精确。

3.) 聚合的初始条件可以只是0

CREATE AGGREGATE aggregate_ee_income_tax(float8)
(
  sfunc     = float8pl
 ,stype     = float8
 ,initcond  = 0
 ,finalfunc = eeincometax
);

4.) 在您的情况下,(least(sum(tax), 256) * 21) / 79 可能比您的自定义聚合更快。 PostgreSQL 提供的聚合函数是用 C 语言编写的,并针对性能进行了优化。我会改用它。

【讨论】:

  • 感谢您提供更多信息。只是一个问题 - 因为我的自定义聚合函数依赖于 postgreSQL 自己的 float8pl 函数,正如我从你的帖子中理解的那样,它也是用 C 编写并优化的 - 我的函数真正增加了多少实际开销?
  • @Zayatzz:最好只是测试一下。使用EXPLAIN ANALYZE 运行这两个变体 - 可能几次以排除缓存影响。我会对比较感兴趣...
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-11-29
  • 1970-01-01
  • 2017-04-09
  • 2013-01-31
  • 2021-11-29
相关资源
最近更新 更多