In the aggregate results from the Tom Kyte's STRAGG tutorial the delimited values in the string produced for Group 4, i.e. 'a,b,b,a', were in no particular order. While the analytic version of STRAGG allowed us to control the sort order easily, no aggregate functions, including STRAGG, provide a built-in concept of sort order.
To control the sort order when STRAGG is used in its aggregate form an
enhanced version of STRAGG_TYPE is required
(changes from the prior version are indicated with
-- deleted
and
-- added.
create or replace type stragg_type as object
(
-- string varchar2(4000), -- deleted
val_table varchar2_table_type , -- added
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 stragg_type
is
static function ODCIAggregateInitialize
( sctx in out stragg_type )
return number
is
begin
-- sctx := stragg_type( null ) ; -- deleted
sctx := stragg_type( varchar2_table_type() ); -- added
return ODCIConst.Success ;
end;
member function ODCIAggregateIterate
( self in out stragg_type ,
value in varchar2
) return number
is
begin
-- self.string := self.string || ',' || value; -- deleted
self.val_table.extend; -- added
self.val_table(self.val_table.count) := value; -- added
return ODCIConst.Success;
end;
member function ODCIAggregateTerminate
( self in stragg_type ,
returnvalue out varchar2 ,
flags in number
) return number
is
v_data varchar2(4000); -- added
begin
-- returnValue := ltrim(self.string, ','); -- deleted
for x in -- added
( -- added
select column_value -- added
from table(val_table) -- added
order by 1 -- added, define sort order here
) -- added
loop -- added
v_data := v_data || ',' || x.column_value;-- added
end loop; -- added
returnValue := ltrim( v_data, ',' ); -- added
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; -- deleted
for i in 1 .. ctx2.val_table.count -- added
loop -- added
self.val_table.extend; -- added
self.val_table(self.val_table.count) := ctx2.val_table(i); -- added
end loop; -- added
-- in 10g this for loop could probably be replaced with the following
-- self.val_table := self.val_table multiset union ( ctx2.val_table ) ;
return ODCIConst.Success;
end;
end;
/
create or replace function stragg
( input varchar2 )
return varchar2
deterministic
parallel_enable
aggregate using stragg_type
;
/
With this new version of the solution the delimited values for Group 4 will appear in ascending order, 'a,a,b,b'.
set null "(null)" select group_key , STRAGG( VAL ) as string from t group by group_key ;
GROUP_KEY STRING ---------- --------------- Group 1 a Group 2 a,b Group 3 a,b,c Group 4 a,a,b,b Group 5 a,b,d,e Group 6 (null) Group 7 (null) Group 8 a,a,b,b (null) (null)
