Author Book Author Books
--------+-------- --------+--------
poly | A poly | A,B,C
poly | B amy | D,E
poly | C ====>
amy | D
amy | E
HOW?
select author, to_list(book) as books
from table_name
group by author;
SO SIMPLE?
Yes, we just need to create a user-defined aggregate function as follows:
1. create type object
create or replace
type tolist as object
(
list varchar2(2000),
static function ODCIAggregateInitialize
( actx in out tolist
) return number,
member function ODCIAggregateIterate
( self in out tolist ,
value in varchar2
) return number,
member function ODCIAggregateTerminate
( self in tolist ,
returnValue out varchar2,
flags in varchar2
) return number,
member function ODCIAggregateMerge
(self in out tolist ,
ctx2 in tolist
) return number
)
2. create type body
create or replace
type body tolist is
static function ODCIAggregateInitialize
( actx in out tolist
) return number is
begin
actx := tolist('');
return ODCIConst.Success;
end;
member function ODCIAggregateIterate
( self in out tolist,
value in varchar2
) return number is
begin
if self.list is null then self.list := value;
else
self.list := self.list || ',' || value;
end if;
return ODCIConst.Success;
end;
member function ODCIAggregateTerminate
( self in tolist,
returnValue out varchar2,
flags in varchar2
) return number is
begin
returnValue:= self.list;
return ODCIConst.Success;
end;
member function ODCIAggregateMerge
(self in out tolist,
ctx2 in tolist
) return number is
begin
if ctx2.list <> '' then
self.list := self.list || ',' || ctx2.list;
end if;
return ODCIConst.Success;
end;
end;
3. create function
CREATE OR REPLACE FUNCTION to_list
( x varchar2
) RETURN varchar2
PARALLEL_ENABLE
AGGREGATE USING tolist;
done
---------------------------------------------
http://download-west.oracle.com/docs/cd/B10501_01/appdev.920/a96595/dci11agg.htm#1005029
http://www.oracle.com/technology/oramag/oracle/06-jul/o46sql.html
==========================
Supplement
"Displaying multiple records in one row" give a summary :
1. SELECT wmsys.wm_concat(dname) departments FROM dept; (10g)
2. A way to get around the restriction that user-defined aggregates may only have one argument, which allows you to specify an alternative separator character.
3. A really delicate way using just sql
SELECT deptno
, LTRIM(MAX(SYS_CONNECT_BY_PATH(ename,','))
KEEP (DENSE_RANK LAST ORDER BY curr),',') AS concatenated
FROM ( SELECT deptno
, ename
, ROW_NUMBER() OVER (PARTITION BY deptno ORDER BY ename) AS curr
, ROW_NUMBER() OVER (PARTITION BY deptno ORDER BY ename) -1 AS prev
FROM emp )
GROUP BY deptno
CONNECT BY prev = PRIOR curr AND deptno = PRIOR deptno
START WITH curr = 1;
4. Another approach involves harnessing the dark power of XML
SELECT deptno
, RTRIM
( xmlagg (xmlelement (c, ename || ',') order by ename).extract ('//text()')
, ',' ) AS concatenated
FROM emp
GROUP BY deptno;