Как использовать LISTAGG с UDT в SQL (оракул)? - PullRequest
0 голосов
/ 07 декабря 2018

Кажется, что функция LISTAGG не работает с UDT, как я могу преодолеть эту проблему?

Во-первых, посмотрите на мой 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;

Что я хочучтобы показать каждый элемент таблицы в одной строке, например:

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

Но вместо этого я получаю эту ошибку:

Rapport d'erreur -

Ошибка SQL: ORA-00932: несовместимые типы данных: ожидается, что NUMBER получил USER.OBJTYPE

00000 - "несовместимые типы данных: ожидается, что% s получил% s"

Должен ли я использовать другую функцию?что это тогда?

Спасибо за ваш ответ.

Ответы [ 2 ]

0 голосов
/ 10 декабря 2018

1) Добавить функцию для создания «текстового» представления объекта.

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) Создание пользовательских агрегатов - сложный и мощный

ImpAggr реализует собственный механизм агрегирования.

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 FUNCTION CustomAggregation (input objtype) RETURN varchar2 
PARALLEL_ENABLE AGGREGATE USING ImpAggr;

И использование.

select CustomAggregation(t.col1) from myTab t;
0 голосов
/ 07 декабря 2018

Чтобы использовать listagg с вашим UDT, вам нужно как-то сериализовать ваши данные.От всей души вы могли бы сделать следующее для создания XMLType из ваших объектов, а затем вы можете использовать функцию getStringVal () для объектов XML - или вы можете добавить функцию-член toString к вашим объектам.

например:

select listagg(xmltype(t.col1).getStringVal(),', ') within group (order by 1) from myTab t;
..... 
<OBJTYPE><ID>1</ID><COL1>col1</COL1><COL2>10</COL2></OBJTYPE>, <OBJTYPE><ID>2</ID><COL1>col2</COL1><COL2>20</COL2></OBJTYPE>, <OBJTYPE><ID>3</ID><COL1>col3</COL1><COL2>3000</COL2></OBJTYPE>

или вы можете использовать XMLAGG (с или без getStringVal ())

select xmlagg(xmltype(t.col1)).getStringVal() from myTab t;
...