I have a table (actually a view) with few varchar2 fields. Say,
v_report(id, name, profile_id, profile_name);
I need to collect id-groupped data into a string. So, I do:
SELECT
id,
'Name: ' || clobagg(DISTINCT name)
|| ' Profile_name: ' || clobagg(DISTINCT profile_name)
as description
FROM
v_report
GROUP BY
id
clobagg is like described here: https://community.oracle.com/thread/2264483
(especially use clobagg not stragg due to it's returning value clob able to store >4000 chars)
It works fine when concatenated profile string isn't too large. But if it is i get fair Oracle exception:
ORA-22835: Buffer too small for CLOB to CHAR or BLOB to RAW conversion (actual: 7680, maximum: 4000)
Is there some way out? I mean something like DBMS_LOB.APPEND function but available to call via SQL. Or any other way to concat many varchar2 strings into a large CLOB string w/o length limit.
Thanks in advance.
Pavel.
NOTE: initially posted as a Comment, but offered as an Answer now, since the OP has confirmed that this was, in fact, the problem.
So, are you able to create the CLOBs in the first place (without concatenation)? If you are, then what you are missing is wrapping the literals (and any other VARCHAR2 values you may have) within to_clob(). Good luck!
Minor addition.
After this solving I got same error in clobagg;
So I had to modify it to set input parameter as varchar2 (to enable DISTINCT clause work) and the returning value is CLOB.
So, as far it may be useful here it is:
create or replace
TYPE CLOBAGG_TYPE AS OBJECT
(
text clob,
static function ODCIAggregateInitialize(
sctx in out clobagg_type
)
return number,
member function ODCIAggregateIterate(
self in out clobagg_type,
value in varchar2
)
return number,
member function ODCIAggregateTerminate(
self in clobagg_type,
returnvalue out clob,
flags in varchar2
)
return number,
member function ODCIAggregateMerge(
self in out clobagg_type,
ctx2 in clobagg_type
)
return number
)
create or replace
TYPE BODY CLOBAGG_TYPE AS
static function ODCIAggregateInitialize(
sctx in out clobagg_type
)
return number
is
begin
sctx := clobagg_type(null) ;
return ODCIConst.Success ;
end;
member function ODCIAggregateIterate(
self in out clobagg_type,
value in varchar2
)
return number
is
begin
self.text := self.text || ', ' || value;
return ODCIConst.Success;
end;
member function ODCIAggregateTerminate(
self in clobagg_type,
returnvalue out clob,
flags in varchar2
)
return number
is
begin
returnValue := ltrim(self.text,', ');
return ODCIConst.Success;
end;
member function ODCIAggregateMerge(
self in out clobagg_type ,
ctx2 in clobagg_type
)
return number
is
begin
self.text := self.text || ctx2.text;
return ODCIConst.Success;
end;
END;
create or replace
FUNCTION CLOBAGG(
input varchar2
) RETURN clob
deterministic
parallel_enable
aggregate using clobagg_type;
Related
I am using Oracle 12.1.
I have an ID column on which I am using group by and want to convert values in another column (say NAME) to comma seperated string as a CLOB (not VARCHAR2 because of its limitation to 4000 chars).
I tried with LISTAGG function but it fails as the the comma seperated string is more than 4000 chars. (There is an improved version of LISTAGG to restrict the overflow, but is not available in Oracle 12.1)
With XMLAGG, it works but I don't want to use XMLAGG because this perticular function is called every 5 seconds and is giving performance issues sometimes and alsoonce in a while "ORA-04036: PGA memory used by the instance exceeds PGA_AGGREGATE_LIMIT"
What I would like to have is -
Either need a way to convert column values to comma seperated string as a CLOB (without using LISTAGG, XMLAGG)
OR
I am fine with skipping some column values and use "..." to inform that there are more values.
(lets say we can consider only 5 rows instead of all rows for given ID (group by column))
Thank you in advance!
From my answer here, you can write a custom aggregation function to aggregate VARCHAR2s into a CLOB:
CREATE OR REPLACE TYPE CLOBAggregation AS OBJECT(
value CLOB,
STATIC FUNCTION ODCIAggregateInitialize(
ctx IN OUT CLOBAggregation
) RETURN NUMBER,
MEMBER FUNCTION ODCIAggregateIterate(
self IN OUT CLOBAggregation,
value IN VARCHAR2
) RETURN NUMBER,
MEMBER FUNCTION ODCIAggregateTerminate(
self IN OUT CLOBAggregation,
returnValue OUT CLOB,
flags IN NUMBER
) RETURN NUMBER,
MEMBER FUNCTION ODCIAggregateMerge(
self IN OUT CLOBAggregation,
ctx IN OUT CLOBAggregation
) RETURN NUMBER
);
/
CREATE OR REPLACE TYPE BODY CLOBAggregation
IS
STATIC FUNCTION ODCIAggregateInitialize(
ctx IN OUT CLOBAggregation
) RETURN NUMBER
IS
BEGIN
ctx := CLOBAggregation( NULL );
RETURN ODCIConst.SUCCESS;
END;
MEMBER FUNCTION ODCIAggregateIterate(
self IN OUT CLOBAggregation,
value IN VARCHAR2
) RETURN NUMBER
IS
BEGIN
IF value IS NULL THEN
NULL;
ELSIF self.value IS NULL THEN
self.value := value;
ELSE
self.value := self.value || ',' || value;
END IF;
RETURN ODCIConst.SUCCESS;
END;
MEMBER FUNCTION ODCIAggregateTerminate(
self IN OUT CLOBAggregation,
returnValue OUT CLOB,
flags IN NUMBER
) RETURN NUMBER
IS
BEGIN
returnValue := self.value;
RETURN ODCIConst.SUCCESS;
END;
MEMBER FUNCTION ODCIAggregateMerge(
self IN OUT CLOBAggregation,
ctx IN OUT CLOBAggregation
) RETURN NUMBER
IS
BEGIN
IF self.value IS NULL THEN
self.value := ctx.value;
ELSIF ctx.value IS NULL THEN
NULL;
ELSE
self.value := self.value || ',' || ctx.value;
END IF;
RETURN ODCIConst.SUCCESS;
END;
END;
/
CREATE FUNCTION CLOBAgg( value VARCHAR2 )
RETURN CLOB
PARALLEL_ENABLE AGGREGATE USING CLOBAggregation;
/
Then you can do:
SELECT id,
CLOBAGG( name ) AS names
FROM (
SELECT id,
name
FROM your_table
ORDER BY your_ordering_column
)
GROUP BY id;
OR
I am fine with skipping some column values and use "..." to inform that there are more values. (lets say we can consider only 5 rows instead of all rows for given ID (group by column))
SELECT id,
LISTAGG(
CASE rn WHEN 6 THEN '...' ELSE name END,
','
) WITHIN GROUP (ORDER BY rn) AS names
FROM (
SELECT id,
name,
ROW_NUMBER() OVER (PARTITION BY id ORDER BY your_ordering_column) AS rn
FROM your_table
)
WHERE rn <= 6
GROUP BY id;
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
00000 - "inconsistent datatypes: expected %s got %s"
Should i use another function ? what is it then ?
Thanks for your response.
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;
To use listagg with your UDT you need to serialize your data somehow.
From the top of my head you could do as follows to produce an XMLType from your objects and then you could use getStringVal() function on the XML objects - or you could add a toString member function to your objects.
for example:
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>
or alternatively you can use XMLAGG (with or without getStringVal())
select xmlagg(xmltype(t.col1)).getStringVal() from myTab t;
My DBA is upgrading my oracle db from v10 to v12.
I have some old SP's that uses wm_concat and I need to change it to listagg.
The problematic code is this:
Select registration_id,package_set_id,
REPLACE(REPLACE(WM_CONCAT(REPLACE( (case when ROW_NUMBER() over (partition by product_id,product_detail_set_id,registration_id,product_family_id,application_id,package_Set_id,
legal_status order by packset_country)=1 then legal_status else null end), ',' , '#')) OVER (PARTITION BY PRODUCT_ID, PRODUCT_DETAIL_SET_ID,
REGISTRATION_ID ,PRODUCT_FAMILY_ID,APPLICATION_ID,PACKAGE_SET_ID ORDER BY Packset_country ), ',' , ' | '), '#', ',') as legal_status,
(REPLACE(REPLACE(WM_CONCAT(REPLACE(ev_code, ',' , '#')) OVER (PARTITION BY PRODUCT_ID, PRODUCT_DETAIL_SET_ID,
REGISTRATION_ID ,PRODUCT_FAMILY_ID,APPLICATION_ID,PACKAGE_SET_ID ORDER BY ev_code ), ',' , ' | '), '#', ',')) EV_CODES,
min(marketed_date) over (PARTITION BY PRODUCT_ID, PRODUCT_DETAIL_SET_ID,REGISTRATION_ID ,PRODUCT_FAMILY_ID,APPLICATION_ID,PACKAGE_SET_ID) as marketed_date,
(REPLACE(REPLACE(WM_CONCAT(REPLACE(Packset_country, ',' , '#')) OVER (PARTITION BY PRODUCT_ID, PRODUCT_DETAIL_SET_ID, REGISTRATION_ID ,PRODUCT_FAMILY_ID,
APPLICATION_ID,PACKAGE_SET_ID ORDER BY Packset_country, reg_packset_country_id ), ',' , ' | '), '#', ',')) REGISTRATION_PACKSET_COUNTRIES,
ROW_NUMBER() OVER (PARTITION BY PRODUCT_ID, PRODUCT_DETAIL_SET_ID,REGISTRATION_ID ,PRODUCT_FAMILY_ID,APPLICATION_ID,PACKAGE_SET_ID
ORDER BY Packset_country desc ,reg_packset_country_id) ROW_NUM,
REPLACE(REPLACE(WM_CONCAT(REPLACE( (case when currently_marketed_in_country='Y' then packset_country end), ',' , '#')) OVER (PARTITION BY PRODUCT_ID, PRODUCT_DETAIL_SET_ID,
REGISTRATION_ID ,PRODUCT_FAMILY_ID,APPLICATION_ID,PACKAGE_SET_ID ORDER BY packset_country ,currently_marketed_in_country,reg_packset_country_id ), ',' , ' | '), '#', ',') as CURRENTLY_MARKETED_COUNTRIES
from radw_dwh.dw202_fact_reg_pack_countries
The expected result is:
I tried to change it but there is a problem when I'm trying to use "ROW_NUMBER()" in side "LISTAGG".
How can I fix this?
The basic syntax of LISTAGG is:
LISTAGG(col_name_to_be_aggregated, ',') WITHIN GROUP (ORDER BY col)
In your case, since you have a sub-query as result set to WM_CONCAT, you could put the same sub-query in place of col_name_to_be_aggregated in LISTAGG.
I think you can also get rid of all the REPLACE functions, since, LISTAGG can accept the delimiter of your choice.
Try,
LISTAGG
(
CASE
WHEN ROW_NUMBER() OVER (PARTITION BY product_id,
product_detail_set_id,
registration_id,
product_family_id,
application_id,
package_Set_id,
legal_status
order by packset_country)=1 THEN
legal_status
ELSE
NULL
END), ',') WITHIN GROUP (ORDER BY required_col)
Also, I would like to explain why you need to move to LISTAGG in 12c. Since t has been removed from the latest 12c version. Therefore, any application which has had been relying on WM_CONCAT function will not work once upgraded to 12c. Read Why not use WM_CONCAT function in Oracle?
For pre-11g Release 2, you can't use LISTAGG. There are many string aggregation techniques, have a look at my answer here.
More details about Oracle String Aggregation Techniques
Motivation for the following approach
We had several problems with wm_concat (known bugs) or wm_concat-to-list_agg-conversion-for-12c-use.
(Not to mention the nice compact/declarative usage using wm_concat in many scenarios where other solutions may be quite complicated to read.)
(Sometimes having to use xmlagg because of > 4000 chars/clob problems or regexp_replace workarounds for the wm_concat( distinct ... ) emulation), e.g. like here.
Strategy
So finally in my opinion a rather nice strategy (which may differ depending on your environment/needs/strategy) is, to
create two functions
create function wm_concat_32767(... (working on varchar(32767) which is possible since Oracle 12c) and
depending on MAX_STRING_SIZE of your db you may want to adopt this to "4000" or others
create function wm_concat_clob(... in your db in the sys-schema first.
They should be based on code like provided by the answer from Dart XKey (maybe copied from asktom/Tom Kyte)
create public synonym wm_concat for sys.wm_concat_32767
(I would generally let the wm_concat point to the likely faster wm_concat_32767 rather than wm_concat_clob)
this allows the easy reuse/migration of existing wm_concat(varchar(4000)) based < 11.2g code/usage
one might already know the name of the function and thus easy to get use to
(the problem that online documentation about it may be misleading is what I could live with since the benefits would outweight the disadvantages for me in general)
it will even allow the use of wm_concat( distinct ... ) queries with compact declarative syntax
as opposed to the listagg/xmlagg/regexp_replace-based workarounds mentioned above
create public synonym wm_concat_clob for sys.wm_concat_clob
give proper public execution rights to the wm_concat_*functions
Code
So finally the adopted code we use (maybe I'll update everything mentioned above over time):
wm_concat_32767(...) creation:
depending on MAX_STRING_SIZE of your db you may want to adopt this to "4000" or other values
_
create or replace type string_agg_type as object (
total varchar2(32767),
static function ODCIAggregateInitialize(sctx IN OUT string_agg_type ) return number,
member function ODCIAggregateIterate(self IN OUT string_agg_type , value IN varchar2 ) return number,
member function ODCIAggregateTerminate(self IN string_agg_type, returnValue OUT varchar2, flags IN number) return number,
member function ODCIAggregateMerge(self IN OUT string_agg_type, ctx2 IN string_agg_type) return number
);
/
create or replace type body string_agg_type is static function odciaggregateinitialize(sctx IN OUT string_agg_type) return number is begin sctx := string_agg_type(null); return odciconst.success; end;
member function odciaggregateiterate(self IN OUT string_agg_type, value IN varchar2) return number is begin self.total := self.total || ',' || value; return odciconst.success; end;
member function odciaggregateterminate(self IN string_agg_type, returnvalue OUT varchar2, flags IN number) return number is begin returnvalue := ltrim(self.total, ','); return odciconst.success; end;
member function odciaggregatemerge(self IN OUT string_agg_type, ctx2 IN string_agg_type) return number is begin self.total := self.total || ctx2.total; return odciconst.success; end;
end;
/
CREATE or replace FUNCTION wm_concat_32767(input varchar2) RETURN varchar2 PARALLEL_ENABLE AGGREGATE USING string_agg_type;
/
wm_concat_clob(...) creation based on this code from Michel Cadot:
create or replace type stragg_type4 as object (
result CLOB,
static function ODCIAggregateInitialize (sctx IN OUT stragg_type4) return number,
member function ODCIAggregateIterate (self IN OUT stragg_type4, value IN varchar2) return number,
member function ODCIAggregateTerminate (self IN stragg_type4, returnValue OUT CLOB, flags IN number) return number,
member function ODCIAggregateMerge (self IN OUT stragg_type4, ctx2 IN stragg_type4) return number
);
/
create or replace type body stragg_type4 is
static function ODCIAggregateInitialize (sctx IN OUT stragg_type4) return number is begin
sctx := stragg_type4 (null);
dbms_lob.createtemporary (lob_loc => sctx.result, cache => TRUE, dur => dbms_lob.call);
return ODCIConst.Success;
end;
member function ODCIAggregateIterate (self IN OUT stragg_type4, value IN varchar2) return number is begin
self.result := self.result || ',' || value;
return ODCIConst.Success;
end;
member function ODCIAggregateTerminate (self IN stragg_type4, returnValue OUT CLOB, flags IN number) return number is begin
returnValue := ltrim (self.result, ',');
return ODCIConst.Success;
end;
member function ODCIAggregateMerge (self IN OUT stragg_type4, ctx2 IN stragg_type4) return number is begin
self.result := self.result || ctx2.result;
return ODCIConst.Success;
end;
end;
/
sho err
CREATE or replace FUNCTION wm_concat_clob(input varchar2) RETURN CLOB PARALLEL_ENABLE AGGREGATE USING stragg_type4;
/
As example in code odciaggregate interface in code:
create or replace type string_agg_type as object ( total varchar2(4000),
static function ODCIAggregateInitialize(sctx IN OUT string_agg_type ) return number,
member function ODCIAggregateIterate(self IN OUT string_agg_type , value IN varchar2 ) return number,
member function ODCIAggregateTerminate(self IN string_agg_type, returnValue OUT varchar2, flags IN number) return number,
member function ODCIAggregateMerge(self IN OUT string_agg_type, ctx2 IN string_agg_type) return number );
/
create or replace type body string_agg_type is static function odciaggregateinitialize(sctx IN OUT string_agg_type) return number is begin sctx := string_agg_type(null); return odciconst.success; end;
member function odciaggregateiterate(self IN OUT string_agg_type, value IN varchar2) return number is begin self.total := self.total || ',' || value; return odciconst.success; end;
member function odciaggregateterminate(self IN string_agg_type, returnvalue OUT varchar2, flags IN number) return number is begin returnvalue := ltrim(self.total, ','); return odciconst.success; end;
member function odciaggregatemerge(self IN OUT string_agg_type, ctx2 IN string_agg_type) return number is begin self.total := self.total || ctx2.total; return odciconst.success; end;
end;
/
CREATE or replace FUNCTION stragg(input varchar2) RETURN varchar2 PARALLEL_ENABLE AGGREGATE USING string_agg_type;
/
with t as ( select 'a1' val from dual union all select 'b2' val from dual ) select stragg(val) as val from t; val --------------------------- a1,b2
This question already has answers here:
Oracle: Combine multiple results in a subquery into a single comma-separated value [duplicate]
(4 answers)
Closed 7 years ago.
Is there a simple way to do a string aggregation without using ListAgg? I am new to Oracle and I was trying to use the ListAgg function and it is not working. I think I have a version installed that is not compatible with ListAgg. I am essentially trying to the following:
Actual List:
Encounter ID CPT Code
------------ ---------
123 21556
124 21554
123 92145
123 92542
Output:
Encounter ID CPT Code
------------ ---------
123 21556,92145,92542
124 21554
This is Tom Kyte's version of Stragg which he no longer uses as listagg is more efficient. This will aggregate strings up to 4000 characters long. You must have the grant to create types and functions in your_schema. The separator cannot be changed dynamically but I leave that as an exercise.
Usage: select dept_no, stragg(emp_name) from emp group by dept_no;
create or replace type Your_schema.stragg_type as object
(
string varchar2(4000),
static function ODCIAggregateInitialize
( sctx in out stragg_type )
return number ,
member function ODCIAggregateIterate
( self in out stragg_type ,
value in varchar2
) return number ,
member function ODCIAggregateTerminate
( self in stragg_type,
returnvalue out varchar2,
flags in number
) return number ,
member function ODCIAggregateMerge
( self in out stragg_type,
ctx2 in stragg_type
) return number
);
/
create or replace type body Your_schema.stragg_type
is
static function ODCIAggregateInitialize
( sctx in out stragg_type )
return number
is
begin
sctx := stragg_type( null ) ;
return ODCIConst.Success ;
end;
member function ODCIAggregateIterate
( self in out stragg_type ,
value in varchar2
) return number
is
begin
self.string := self.string || ',' || value ;
return ODCIConst.Success;
end;
member function ODCIAggregateTerminate
( self in stragg_type ,
returnvalue out varchar2 ,
flags in number
) return number
is
begin
returnValue := ltrim( self.string, ',' );
return ODCIConst.Success;
end;
member function ODCIAggregateMerge
( self in out stragg_type ,
ctx2 in stragg_type
) return number
is
begin
self.string := self.string || ctx2.string;
return ODCIConst.Success;
end;
end;
/
create or replace function Your_schema.stragg
( input varchar2 )
return varchar2
deterministic
parallel_enable
aggregate using stragg_type
;
/
listagg is a function introduced in Oracle 11.2! now this function is bugging us allot, we are migrating from MySQL to Oracle and we have this query:
SELECT
p_id,
MAX(registered) AS registered,
listagg(MESSAGE, ' ') within GROUP (ORDER BY registered) AS MESSAGE
FROM
umm_parent_id_remarks_v m
GROUP BY
m.p_id;
is works fine in MySQL as far as we know
what bugging us is under Oracle it returns VARCAR and not CLOB as we need!
the text is huge and we do need it to be CLOB!
here is what I tried to do!
create a CLOB_T table of CLOB Type!
then create the function
create or replace
function listaggclob (t in clob_t)
return clob
as
ret clob := '';
i number;
begin
i := t.first;
while i is not null loop
if ret is not null then
ret := ret || ' ';
end if;
ret := ret || t(i);
i := t.next(i);
end loop;
return ret;
end;
now if I run it:
SELECT
p_id,
MAX(registered) AS registered,
listaggclob(cast(collect (MESSAGE) as clob_t)) MESSAGE
FROM
umm_parent_id_remarks_v m
GROUP BY
m.p_id;
I get
ORA-22814: attribute or element value is larger than specified in type
is there any solution for it?
thanks you
Use collect or write your own aggregation function.
WM_CONCAT worked for me.
SELECT replace(WMSYS.WM_CONCAT(myTable.name), ',', ';')
FROM myTable
GROUP BY myTable.id
I wrapped it with a "replace" to specify a different item separator (';') from the one used by WM_CONCAT (',').
Use xmlAgg, example is shown below:
SELECT RTRIM(XMLAGG(XMLELEMENT(E,colname,',').EXTRACT('//text()') ORDER BY colname).GetClobVal(),',') AS LIST
FROM tablename;
This will return clob value and so no need to create custom function.
You can solve the ORA-22814 error by using MULTISET instead of COLLECT:
SELECT
p_id,
MAX(registered) AS registered,
listaggclob(cast(multiset(
select MESSAGE
from umm_parent_id_remarks_v
where umm_parent_id_remarks_v.p_id = m.p_id
) as clob_t)) MESSAGE
FROM
umm_parent_id_remarks_v m
GROUP BY
m.p_id;
You might want to look at user-defined aggregate functions.
Differnt string aggregation techniques are shown here. They include an example for user-defined aggregate functions.
-- Creating Clobe Type --
CREATE OR REPLACE TYPE "MSCONCATIMPL_CLOB" AS OBJECT (
resultstring CLOB,
delimiter VARCHAR2(10),
STATIC FUNCTION odciaggregateinitialize ( io_srccontext IN OUT msconcatimpl_clob ) RETURN NUMBER,
MEMBER FUNCTION odciaggregateiterate (
self IN OUT msconcatimpl_clob,
value IN CLOB
) RETURN NUMBER,
MEMBER FUNCTION odciaggregateterminate (
self IN msconcatimpl_clob,
o_returnvalue OUT CLOB,
i_flags IN NUMBER
) RETURN NUMBER,
MEMBER FUNCTION odciaggregatemerge (
self IN OUT msconcatimpl_clob,
i_ctx2 IN msconcatimpl_clob
) RETURN NUMBER
);
/
-- Creating Clobe Type Body --
CREATE OR REPLACE TYPE BODY "MSCONCATIMPL_CLOB" IS
STATIC FUNCTION odciaggregateinitialize ( io_srccontext IN OUT msconcatimpl_clob ) RETURN NUMBER
IS
BEGIN
io_srccontext := msconcatimpl_clob(
NULL,
NULL
);
io_srccontext.delimiter := ' ';
RETURN odciconst.success;
END odciaggregateinitialize;
MEMBER FUNCTION odciaggregateiterate (
self IN OUT msconcatimpl_clob,
value IN CLOB
) RETURN NUMBER
IS
BEGIN
IF
value IS NOT NULL
THEN
IF
self.resultstring IS NULL
THEN
self.resultstring := self.resultstring || value;
ELSE
self.resultstring := self.resultstring
|| self.delimiter
|| value;
END IF;
END IF;
RETURN odciconst.success;
END odciaggregateiterate;
MEMBER FUNCTION odciaggregateterminate (
self IN msconcatimpl_clob,
o_returnvalue OUT CLOB,
i_flags IN NUMBER
) RETURN NUMBER
IS
BEGIN
o_returnvalue := self.resultstring;
RETURN odciconst.success;
END odciaggregateterminate;
MEMBER FUNCTION odciaggregatemerge (
self IN OUT msconcatimpl_clob,
i_ctx2 IN msconcatimpl_clob
) RETURN NUMBER
IS
BEGIN
IF
self.resultstring IS NULL
AND
i_ctx2.resultstring IS NOT NULL
THEN
self.resultstring := i_ctx2.resultstring;
ELSIF
self.resultstring IS NOT NULL
AND
i_ctx2.resultstring IS NOT NULL
THEN
self.resultstring := self.resultstring
|| self.delimiter
|| i_ctx2.resultstring;
END IF;
RETURN odciconst.success;
END odciaggregatemerge;
END;
/
-- Creating Clobe Function --
CREATE OR REPLACE FUNCTION ms_concat_clob ( input VARCHAR2 ) RETURN CLOB
PARALLEL_ENABLE
AGGREGATE USING msconcatimpl_clob;
/