Search code examples
sqloracle-databaseuser-defined-typeslistagg

How to use LISTAGG with UDT in sql (oracle)?


It seems that the LISTAGG function doesn't work with UDT, how can i overcome this problem ?

First, have a look at my sql :

create or replace type objtype as object(
    id int,
    col1 varchar2(30),
    col2 float
);

create table myTab(
    col1 objtype
);

insert into myTab values (objtype(1,'col1',10));
insert into myTab values (objtype(2,'col2',20));
insert into myTab values (objtype(3,'col3',3000));

select listagg(t.col1,', ') within group (order by 1) from myTab t;

What i want is to show every element of the table in one row, like this :

objtype(1,'col1',10) , objtype(2,'col2',20), objtype(3,'col3',3000)

But instead i get this error :

Rapport d'erreur -

Erreur SQL : ORA-00932: inconsistent datatypes: expected NUMBER got USER.OBJTYPE

  1. 00000 - "inconsistent datatypes: expected %s got %s"

Should i use another function ? what is it then ?

Thanks for your response.


Solution

  • 1) Add function to produce "text" representation of object.

    create or replace type objtype as object(
        id int,
        col1 varchar2(30),
        col2 float,
        member function toStr return varchar2
    );
    
    create or replace type body objtype is
        member function toStr return varchar2 is 
        begin 
          return 'objtype('||self.id||','||col1||','||col2||')';
        end;
    end;
    
    select listagg(t.col1.toStr(),', ') within group (order by 1) from myTab t;
    

    2) Create User-defined Aggregates - complicated and powerful

    ImpAggr is implementing custom aggregation mechanism.

    create  or replace type  ImpAggr as object(
      list_of_object varchar2(32000), -- second highest value seen so far
      static function ODCIAggregateInitialize(sctx IN OUT ImpAggr) 
        return number,
      member function ODCIAggregateIterate(self IN OUT ImpAggr, 
        value IN objtype) return number,
      member function ODCIAggregateTerminate(self IN ImpAggr, 
        returnValue OUT varchar2, flags IN number) return number,
      member function ODCIAggregateMerge(self IN OUT ImpAggr, 
        ctx2 IN ImpAggr) return number
    );
    /
    
    create or replace type body ImpAggr is 
    static function ODCIAggregateInitialize(sctx IN OUT ImpAggr) 
    return number is 
    begin
    
       -- Aggregate Initialize
      sctx := ImpAggr(null);
      return ODCIConst.Success;
    end;
    
    member function ODCIAggregateIterate(self IN OUT ImpAggr, value IN objtype) return number is
    begin
      -- Aggregate Iterate
    
      self.list_of_object :=list_of_object || ',objtype('||value.id||','||value.col1||','||value.col2||')';
    
      return ODCIConst.Success;
    end;
    
    member function ODCIAggregateTerminate(self IN ImpAggr, 
        returnValue OUT varchar2, flags IN number) return number is
    begin
      -- retrun result list_of_object.
      returnValue := self.list_of_object;
      return ODCIConst.Success;
    end;
    
    member function ODCIAggregateMerge(self IN OUT ImpAggr, ctx2 IN ImpAggr) return number is
    begin 
      -- merge content only if aggregation process gone in parallel execution
      self.list_of_object := ','||ctx2.list_of_object;
      return ODCIConst.Success;
    end;
    end;
    /
    

    Create aggregation function.

    CREATE FUNCTION CustomAggregation (input objtype) RETURN varchar2 
    PARALLEL_ENABLE AGGREGATE USING ImpAggr;
    

    And usage.

    select CustomAggregation(t.col1) from myTab t;