Oracle 的用户自定义聚合函数可以定义为与两列一起使用吗?

Posted

技术标签:

【中文标题】Oracle 的用户自定义聚合函数可以定义为与两列一起使用吗?【英文标题】:Can Oracle's user-defined aggregation function be defined for use with two columns? 【发布时间】:2015-07-15 15:29:28 【问题描述】:

我想实现一个自定义回归聚合函数,类似于现有的REGR_SLOPE

我要定义的函数需要获取两列作为参数,例如

select 
   T.EMPLOYEE_ID,
   CUSTOM_REGR_SLOPE(T.DATE, T.SALARY) as SALARY_TREND
from (...) T
group by T.EMPLOYEE_ID;

Oracle 的文档表明这可能是不可能的,但我可能不擅长在字里行间阅读;-)。

我们使用 Oracle 12。

【问题讨论】:

您是在问是否可以定义一个接受两个可以从 SQL 调用的参数的函数? @Boneist 自定义聚合函数,从技术上讲是自定义类型;是的。 你试过了吗?您可以从 SQL 调用具有任意多参数的函数(当然,只要该函数满足允许从 SQL 调用它的条件!)。 这是一个 example 为间隔创建自定义聚合函数,您可以将其用作您的模板 一个快速的谷歌抛出this from Tom Kyte,在那里他定义了一条记录,您可以将其作为单个允许的参数传入......并且该记录可以有多个字段 【参考方案1】:

是的,如果您真的想要/需要,它是可能的。你可以这样做:

首先,创建一个对象类型:

create or replace type two_nums_t as object
(
num1 number,
num2 number
);

然后创建您的自定义规范:

CREATE OR REPLACE TYPE TotalSumPair
AS OBJECT (
runningSum1 number,
runningCnt1 number,
runningSum2 number,
runningCnt2 number,

STATIC FUNCTION ODCIAggregateInitialize
  ( actx IN OUT TotalSumPair
  ) RETURN NUMBER,

MEMBER FUNCTION ODCIAggregateIterate
  ( self  IN OUT TotalSumPair,
    val   IN     two_nums_t
  ) RETURN NUMBER,

MEMBER FUNCTION ODCIAggregateTerminate
  ( self             IN   TotalSumPair,
    returnValue  OUT  NUMBER, -- return 
    flags           IN   NUMBER
  ) RETURN NUMBER,

MEMBER FUNCTION ODCIAggregateMerge
  (self  IN OUT TotalSumPair,
   ctx2 IN      TotalSumPair
  ) RETURN NUMBER
);

还有自定义正文:

CREATE OR REPLACE TYPE BODY TotalSumPair AS
STATIC FUNCTION ODCIAggregateInitialize
  ( actx IN OUT TotalSumPair
  ) RETURN NUMBER IS 
  BEGIN
    IF actx IS NULL THEN
      actx := TotalSumPair(0,0,0,0);
    ELSE
      actx.runningSum1 := 0;
      actx.runningCnt1 := 0;
      actx.runningSum2 := 0;
      actx.runningCnt2 := 0;
    END IF;
    RETURN ODCIConst.Success;
  END;

MEMBER FUNCTION ODCIAggregateIterate
  ( self  IN OUT TotalSumPair,
    val   IN     two_nums_t
  ) RETURN NUMBER IS
  BEGIN
    self.runningSum1 := self.runningSum1 + nvl(val.num1,0);
    self.runningSum2 := self.runningSum2 + nvl(val.num2,0);
    self.runningCnt1 := self.runningCnt1 + 1;
    self.runningCnt2 := self.runningCnt2 + 1;
    RETURN ODCIConst.Success;
  END;

MEMBER FUNCTION ODCIAggregateTerminate
  ( self        IN  TotalSumPair,
    ReturnValue OUT NUMBER,
    flags       IN  NUMBER
  ) RETURN NUMBER IS
  BEGIN
    --if (runningCnt1 <> 0) then
        returnValue := (self.runningSum1 + self.runningSum2);
    --else
    --    returnValue := self.runningSum1;
    --end if;
    RETURN ODCIConst.Success;
  END;

MEMBER FUNCTION ODCIAggregateMerge
  (self IN OUT TotalSumPair,
   ctx2 IN     TotalSumPair
  ) RETURN NUMBER IS
  BEGIN
    self.runningSum1 := self.runningSum1 + ctx2.runningSum1;
    self.runningCnt1 := self.runningCnt1 + ctx2.runningCnt1;
    self.runningSum2 := self.runningSum2 + ctx2.runningSum2;
    self.runningCnt2 := self.runningCnt2 + ctx2.runningCnt2;
    RETURN ODCIConst.Success;
  END;

END;

定义你的功能:

CREATE OR REPLACE FUNCTION total_sum_pair( x two_nums_t) 
RETURN number  PARALLEL_ENABLE
AGGREGATE USING TotalSumPair;

现在这样称呼它:

with x as (
 select 'X' as id, 1 as num1, 2 as num2 from dual
 union all
 select 'X' as id, 3 as num1, 4 as num2 from dual
 union all
 select 'Z' as id, 5 as num1, 6 as num2 from dual
)
select id, total_sum_pair(two_nums_t(num1, num2)) sum
from x
group by id;

输出:

ID  SUM
X   10
Z   11

这将每个 X 行 (1+2+3+4) 和每个 Y 行 (5+6) 的两个数字相加。

呸! ;)

【讨论】:

提示如果将“map member function”添加到two_nums_t,您可以使用不同的状态调用它。【参考方案2】:

Oracle's documentation 并不是说​​不可能,而是说:

对聚合使用的限制 如果指定此子句,则只能为函数指定一个输入参数。

正如其他人所指出的,一个输入参数可以是对象类型或集合等;但是你不能定义你自己调用的聚合函数,就像你展示的那样简单,CUSTOM_REGR_SLOPE(T.DATE, T.SALARY)

【讨论】:

以上是关于Oracle 的用户自定义聚合函数可以定义为与两列一起使用吗?的主要内容,如果未能解决你的问题,请参考以下文章

如何定义自定义聚合函数来对一列向量求和?

使用 plsql 的用户定义的自定义聚合函数

Oracle 自定义聚合函数

SqlServer如何用Sql语句自定义聚合函数

为访问查询创建自定义聚合函数

pandas rolling对象的自定义聚合函数