不能在对象中使用oracle关联数组(用于自定义聚合函数)

时间:2016-12-29 20:56:35

标签: oracle

背景:我的目的是在oracle中编写一个聚合函数,使一个字符串包含每个元素的出现次数。例如" Jake:2-Tom:3-Jim:5"应该是杰克2次,汤姆3次,吉姆5次。因此,对于编写自定义聚合函数,我应该编写一个对象实现ODCIAggregate例程。还有一个Map数据结构,用于计算每个元素的出现次数。只有像oracle中的数据结构那样的Map才是关联数组。

问题:遗憾的是,我不知道在对象中使用关联数组的任何方法。我试过这些方法:

1 - 为关联数组创建泛型类型并在对象中使用它。 Oracle不允许创建通用的关联数组类型。

CREATE TYPE STR_MAP IS TABLE OF NUMBER INDEX BY VARCHAR2(100);

这会出现以下错误:

PLS-00355: use of pl/sql table not allowed in this context 

2 - 在包中创建类似地图的地图并在对象中使用它。 Oracle允许在包中创建关联数组,但不允许使用' in包类型'在对象中。我检查了包裹上的授权执行的所有问题,或者在包裹类型'中为'做了同义词。但是在包装类型中无法使用'在对象声明中。

P.S。 1:

当然我们可以通过嵌套group by为一列做到。但我更喜欢只使用agg-func为很多列做这件事。这是非常有用的agg-func,我想知道为什么之前没有人写过这样的东西。对于许多列,我们具有有限数量的不同值,并且使用这样的agg-func,我们可以简单地总结所有这些值。例如,如果我们有一个名为ocur_count()的agg-func,我们可以简单地分析这样的事务集合:

select ocur_count(trans_type), ocur_count(trans_state), ocur_count(response_code), ocur_count(something_status) from transaction;

1 个答案:

答案 0 :(得分:1)

你可以使用listagg和一个简单的group by来计算你需要的东西(注意listagg输出的大小限制为4k chars)。在这里,我计算名字的出现次数,使用','作为名称之间的分隔符,使用':'作为计数的分隔符:

SQL> create table person_test
(
person_id number,
first_name varchar2(50),
last_name varchar2(50)
)
Table created.
SQL> insert into person_test values (1, 'Joe', 'Blow')
1 row created.
SQL> insert into person_test values (2, 'Joe', 'Smith')
1 row created.
SQL> insert into person_test values (3, 'Joe', 'Jones')
1 row created.
SQL> insert into person_test values (4, 'Frank', 'Rizzo')
1 row created.
SQL> insert into person_test values (4, 'Frank', 'Jones')
1 row created.
SQL> insert into person_test values (5, 'Betty', 'Boop')
1 row created.
SQL> commit
Commit complete.
SQL> -- get list of first names and counts into single string
SQL> --
SQL> -- NOTE: Beware of size limitations of listagg (4k chars if
SQL> -- used as a SQL statement I believe)
SQL> --
SQL> select listagg(person_count, ',')
within group(order by person_count) as person_agg
from (
    select first_name || ':' || count(1) as person_count
    from person_test
    group by first_name
    order by first_name
)

PERSON_AGG                                                                      
--------------------------------------------------------------------------------
Betty:1,Frank:2,Joe:3                                                           
1 row selected.

注意:如果您遇到字符串连接问题太长(超过listagg大小限制),您可以选择使用xmlagg返回CLOB:

-- this returns a CLOB
select rtrim(xmlagg(xmlelement(e,person_count,',').extract('//text()') order by person_count).GetClobVal(),',')
from (
    select first_name || ':' || count(1) as person_count
    from person_test
    group by first_name
    order by first_name
);

希望有所帮助

修改

如果您想要多列(本例中的firstname和lastname)的计数,您可以这样做:

select 
typ,
listagg(cnt, ',') within group(order by cnt)
as name_agg
from (
    -- FN=FirstName, LN=LastName
    select 'FN' as typ, first_name || ':' || count(1) as cnt
    from person_test
    group by first_name
    union all
    select 'LN' as typ, last_name || ':' || count(1) as cnt
    from person_test
    group by last_name
)
group by typ;

输出:

"FN"    "Betty:1,Frank:2,Joe:3"
"LN"    "Blow:1,Boop:1,Jones:2,Rizzo:1,Smith:1"

我还注意到你可能可以创建一个自定义聚合函数来实现这一点,我更喜欢先用SQL的内置功能来解决我的问题。