You may have known aggregate functions well, for example, sum, count, max, min, etc. How about add up strings? After searching on the web for a while, I found a function (See the code at the bottom).
The main function is
STRING_AGG, which you can use the same way as other aggregate functions. Example:
select employee_id, string_agg( job_id ) job_list from job_history group by employee_id;
Here's the output in SQL Plus:
EMPLOYEE_ID JOB_LIST
----------- ----------------------------------------
101 AC_ACCOUNT,AC_MGR
102 IT_PROG
114 ST_CLERK
122 ST_CLERK
176 SA_REP,SA_MAN
200 AD_ASST,AC_ACCOUNT
201 MK_REP
7 rows selected.
The delimiter by default is comma (,), and is controlled by a store package
STRING_AGG_CONTROL. To change it, you can use:
begin string_agg_control.g_delim := '|'; end;
/
Then run the sql again:
select employee_id, string_agg( job_id ) job_list from job_history group by employee_id;
EMPLOYEE_ID JOB_LIST
----------- ----------------------------------------
101 AC_ACCOUNT|AC_MGR
102 IT_PROG
114 ST_CLERK
122 ST_CLERK
176 SA_REP|SA_MAN
200 AD_ASST|AC_ACCOUNT
201 MK_REP
7 rows selected.
The function is implemented using an Oracle user defined type with the help of User-Defined Aggregates Interface. You can find more information in
this doc.
Sounds perfect? Not that much -- you cannot order the strings. That means the source strings appear in the aggregate string randomly.
The script is attached:
create or replace package string_agg_control IS
g_delim varchar2(100) := ',';
end;
/
CREATE OR REPLACE TYPE t_string_agg AS OBJECT
(
g_string VARCHAR2(32767),
g_delim VARCHAR2(100), -- Delimiter to seperate the strings
STATIC FUNCTION ODCIAggregateInitialize(sctx IN OUT t_string_agg)
RETURN NUMBER,
MEMBER FUNCTION ODCIAggregateIterate(self IN OUT t_string_agg,
value IN VARCHAR2 )
RETURN NUMBER,
MEMBER FUNCTION ODCIAggregateTerminate(self IN t_string_agg,
returnValue OUT VARCHAR2,
flags IN NUMBER)
RETURN NUMBER,
MEMBER FUNCTION ODCIAggregateMerge(self IN OUT t_string_agg,
ctx2 IN t_string_agg)
RETURN NUMBER
);
/
SHOW ERRORS
CREATE OR REPLACE TYPE BODY t_string_agg IS
STATIC FUNCTION ODCIAggregateInitialize(sctx IN OUT t_string_agg)
RETURN NUMBER IS
BEGIN
sctx := t_string_agg( NULL, string_agg_control.g_delim );
RETURN ODCIConst.Success;
END;
MEMBER FUNCTION ODCIAggregateIterate(self IN OUT t_string_agg,
value IN VARCHAR2 )
RETURN NUMBER IS
BEGIN
SELF.g_string := self.g_string || self.g_delim || value;
RETURN ODCIConst.Success;
END;
MEMBER FUNCTION ODCIAggregateTerminate(self IN t_string_agg,
returnValue OUT VARCHAR2,
flags IN NUMBER)
RETURN NUMBER IS
BEGIN
returnValue := RTRIM(LTRIM(SELF.g_string, self.g_delim ), self.g_delim );
RETURN ODCIConst.Success;
END;
MEMBER FUNCTION ODCIAggregateMerge(self IN OUT t_string_agg,
ctx2 IN t_string_agg)
RETURN NUMBER IS
BEGIN
SELF.g_string := SELF.g_string || self.g_delim || ctx2.g_string;
RETURN ODCIConst.Success;
END;
END;
/
SHOW ERRORS
CREATE OR REPLACE FUNCTION string_agg (p_input VARCHAR2)
RETURN VARCHAR2
PARALLEL_ENABLE AGGREGATE USING t_string_agg;
/
SHOW ERRORS