トップページに戻る    次のSQLパズルへ    前のSQLパズルへ

8-47 distinctオプション指定のwmsys.wm_concatを模倣

SQLパズル

strTable
ID  Val1  Val2  Val3
--  ----  ----  ----
aa  11    22    33
aa  99    22    33
aa  11    22    99
bb  44    55    66
bb  44    55    66
cc  aa    bb    cc
cc  aa    bb    cc
dd  xx    yy    zz
dd  yy    zz    xx
dd  zz    xx    yy
dd  yy    zz    xx

下記のdistinctオプション指定のwmsys.wm_concatを模倣する。

select ID,wmsys.wm_concat(distinct Val1) as Val1,
          wmsys.wm_concat(distinct Val2) as Val2,
          wmsys.wm_concat(distinct Val3) as Val3
  from strTable
group by ID
order by ID;

出力結果
ID  Val1      Val2      Val3
--  --------  --------  --------
aa  11,99     22        33,99
bb  44        55        66
cc  aa        bb        cc
dd  xx,yy,zz  xx,yy,zz  xx,yy,zz


データ作成スクリプト

create table strTable(ID,Val1,Val2,Val3) as
select 'aa','11','22','33' from dual union all
select 'aa','99','22','33' from dual union all
select 'aa','11','22','99' from dual union all
select 'bb','44','55','66' from dual union all
select 'bb','44','55','66' from dual union all
select 'cc','aa','bb','cc' from dual union all
select 'cc','aa','bb','cc' from dual union all
select 'dd','xx','yy','zz' from dual union all
select 'dd','yy','zz','xx' from dual union all
select 'dd','zz','xx','yy' from dual union all
select 'dd','yy','zz','xx' from dual;


SQL

col Val1 for a10
col Val2 for a10
col Val3 for a10

--■■■XMLの関数を使う方法■■■
select ID,
Rtrim(substr(replace(replace(XMLAgg(XMLElement("dummy",Val1) order by Val1)
                            ,'</dummy>'),'<dummy>',','),2),',') as Val1,
Rtrim(substr(replace(replace(XMLAgg(XMLElement("dummy",Val2) order by Val2)
                            ,'</dummy>'),'<dummy>',','),2),',') as Val2,
Rtrim(substr(replace(replace(XMLAgg(XMLElement("dummy",Val3) order by Val3)
                            ,'</dummy>'),'<dummy>',','),2),',') as Val3
from (select ID,
      case Row_Number() over(partition by ID,Val1 order by 1) when 1 then Val1 end as Val1,
      case Row_Number() over(partition by ID,Val2 order by 1) when 1 then Val2 end as Val2,
      case Row_Number() over(partition by ID,Val3 order by 1) when 1 then Val3 end as Val3
        from strTable)
group by ID
order by ID;

--■■■model句を使う方法(10g以降)■■■
select ID,
LTrim(max(Val1),',') as Val1,LTrim(max(Val2),',') as Val2,LTrim(max(Val3),',') as Val3
from (select ID,Val1,Val2,Val3
        from strTable
       model
      partition by(ID)
      dimension by(Row_Number() over(partition by ID order by 1) as soeji)
      measures(cast(case Row_Number() over(partition by ID,Val1 order by 1)
                    when 1 then Val1 end as varchar2(20)) as Val1,
               cast(case Row_Number() over(partition by ID,Val2 order by 1)
                    when 1 then Val2 end as varchar2(20)) as Val2,
               cast(case Row_Number() over(partition by ID,Val3 order by 1)
                    when 1 then Val3 end as varchar2(20)) as Val3)
      rules(
      Val1[any] order by soeji = nvl2(Val1[cv(soeji)]
                                     ,Val1[cv(soeji)-1] || ',' || Val1[cv(soeji)]
                                     ,Val1[cv(soeji)-1]),
      Val2[any] order by soeji = nvl2(Val2[cv(soeji)]
                                     ,Val2[cv(soeji)-1] || ',' || Val2[cv(soeji)]
                                     ,Val2[cv(soeji)-1]),
      Val3[any] order by soeji = nvl2(Val3[cv(soeji)]
                                     ,Val3[cv(soeji)-1] || ',' || Val3[cv(soeji)]
                                     ,Val3[cv(soeji)-1])))
group by ID
order by ID;


解説

インラインビューで連結対象外の文字列をnullに変換しています。