別個の値を返すOracleのLISTAGG


94

LISTAGGOracleで関数を使用しようとしています。その列の個別の値のみを取得したいと思います。関数やプロシージャを作成せずに個別の値のみを取得できる方法はありますか?

  col1 col2 Created_by
   1 2スミス 
   1 2ジョン 
   1 3アジェイ 
   1 4ラム 
   1 5ジャック 

col1とLISTAGGcol2 を選択する必要があります(列3は考慮されません)。そうすると、LISTAGG次のような結果になります。[2,2,3,4,5]

ここで重複する「2」を削除する必要があります。col1に対してcol2の個別の値のみが必要です。



サンプルから予想される出力(行)を表示できますか?col1に複数の値がある場合、何を見たいですか?
a_horse_with_no_name 2012

LISTAGGの予想される出力は[2,3,4,5]です。2番目の「2」は削除する必要があります。そして、私のテーブルには1000以上の行があります。
Priyanth 2012

col1に複数の値がある場合、何を見たいですか?
a_horse_with_no_name 2012

コードは次のようになります:-SELECT col1、LISTAGG(col2、 '、')within group(order by col2)FROM table T WHERE ...コンマ。
Priyanth 2012

回答:


76

19c以降:

select listagg(distinct the_column, ',') within group (order by the_column)
from the_table

18c以前:

select listagg(the_column, ',') within group (order by the_column)
from (
   select distinct the_column 
   from the_table
) t

さらに列が必要な場合は、次のようなものを探している可能性があります。

select col1, listagg(col2, ',') within group (order by col2)
from (
  select col1, 
         col2,
         row_number() over (partition by col1, col2 order by col1) as rn
  from foo
  order by col1,col2
)
where rn = 1
group by col1;

2
私も思っていたのと同じです。listaggがクエリ内の唯一の集約関数である場合、これは実行する必要があります。ただし、他の集計関数と組み合わせると、よりトリッキーになります。
Andriy M

はい。私のクエリはこれに似ています。
Priyanth

1
@a_horse_with_no_name:上記の選択ステートメントでは、重複した値が表示されます。重複を削除したい。col1 col2 Created by 1 2 Smith 1 2 John 1 3 Ajay 1 4 Ram 1 5 Jack col1とLISTAGGを選択する必要があります(列3は考慮されません)。その間、次のような結果が得られますOD LISTAGG:-> [2,2,3,4,5]ここで重複する「2」を削除する必要があります。col1に対してcol2の個別の値のみが必要です。
Priyanth 2012

@a_horse_with_no_name:コードを試してみましたが、以下のようなエラーメッセージが表示されましたORA-01489:文字列連結の結果が長すぎます01489。00000-「文字列連結の結果が長すぎます」サイズ。
Priyanth 2012

@Priyanth:あなたは運が悪い。全長が4000バイトを超えており、Oracleはそれを処理できません。アプリケーションコードで集計を行う必要があります。
a_horse_with_no_name 2012

47

問題を解決する方法は次のとおりです。

select  
      regexp_replace(
    '2,2,2.1,3,3,3,3,4,4' 
     ,'([^,]+)(,\1)*(,|$)', '\1\3')

from dual

戻り値

2,2.1,3,4

オラクル19Cからそれはここに組み込まれて組み込まれています

18C以前からグループ内で試してみるここを参照

それ以外の場合は正規表現を使用

以下の回答:

select col1, 

regexp_replace(
    listagg(
     col2 , ',') within group (order by col2)  -- sorted
    ,'([^,]+)(,\1)*(,|$)', '\1\3') )
   from tableX
where rn = 1
group by col1; 

注:上記はほとんどの場合に機能します。リストはソートする必要があります。データによっては、すべての後続スペースと先行スペースをトリムする必要がある場合があります。

20を超えるグループまたは大きな文字列サイズのアイテムがたくさんある場合、Oracleの文字列サイズ制限「文字列連結の結果が長すぎる」に遭遇する可能性があります。

Oracle 12cR2から、このエラーを抑制することができますここを参照してください。または、各グループのメンバーに最大数を設定します。これは、最初のメンバーのみをリストしてもよい場合にのみ機能します。非常に長い変数文字列がある場合、これは機能しない可能性があります。実験する必要があります。

select col1,

case 
    when count(col2) < 100 then 
       regexp_replace(
        listagg(col2, ',') within group (order by col2)
        ,'([^,]+)(,\1)*(,|$)', '\1\3')

    else
    'Too many entries to list...'
end

from sometable
where rn = 1
group by col1;

うまくいけばオラクルの文字列サイズ制限を回避する別の解決策(それほど単純ではない)-文字列サイズは4000に制限されています。この投稿のおかげuser3465996

select col1  ,
    dbms_xmlgen.convert(  -- HTML decode
    dbms_lob.substr( -- limit size to 4000 chars
    ltrim( -- remove leading commas
    REGEXP_REPLACE(REPLACE(
         REPLACE(
           XMLAGG(
             XMLELEMENT("A",col2 )
               ORDER BY col2).getClobVal(),
             '<A>',','),
             '</A>',''),'([^,]+)(,\1)*(,|$)', '\1\3'),
                  ','), -- remove leading XML commas ltrim
                      4000,1) -- limit to 4000 string size
                      , 1)  -- HTML.decode
                       as col2
 from sometable
where rn = 1
group by col1;

V1-一部のテストケース-FYI

regexp_replace('2,2,2.1,3,3,4,4','([^,]+)(,\1)+', '\1')
-> 2.1,3,4 Fail
regexp_replace('2 ,2 ,2.1,3 ,3 ,4 ,4 ','([^,]+)(,\1)+', '\1')
-> 2 ,2.1,3,4 Success  - fixed length items

V2-アイテム内に含まれるアイテム。2,21

regexp_replace('2.1,1','([^,]+)(,\1)+', '\1')
-> 2.1 Fail
regexp_replace('2 ,2 ,2.1,1 ,3 ,4 ,4 ','(^|,)(.+)(,\2)+', '\1\2')
-> 2 ,2.1,1 ,3 ,4  -- success - NEW regex
 regexp_replace('a,b,b,b,b,c','(^|,)(.+)(,\2)+', '\1\2')
-> a,b,b,c fail!

v3-正規表現はIgorに感謝します!すべてのケースで機能します。

select  
regexp_replace('2,2,2.1,3,3,4,4','([^,]+)(,\1)*(,|$)', '\1\3') ,
---> 2,2.1,3,4 works
regexp_replace('2.1,1','([^,]+)(,\1)*(,|$)', '\1\3'),
--> 2.1,1 works
regexp_replace('a,b,b,b,b,c','([^,]+)(,\1)*(,|$)', '\1\3')
---> a,b,c works

from dual

3
公正な結果ですが、それほど単純ではありません。深刻なデータサイズでは、に遭遇しますORA-01489: result of string concatenation is too long
Pero

1
単純ではありませんが、非常に魅力的なソリューションとは言えません。一致文字列が置換文字列だけでなく検索文字列にも使用できることを知りませんでした。ブライアント。
Peter Krassoi、2014

1
注意点として、この方法では値をソートして、複製された値が連続するようにする必要があります。それ以外の場合は失敗します。でもシンプルはいいです!そして、私は特定のケースでこの方法を使用しています。ありがとう!
StewS2 2016年

2
超シンプルは3回以上の繰り返しでは機能しません!、例えばa,b,b,b,b,cになるa,b,b,c:-((オラクル11.2)
アンドレアス・ディートリッヒ

4
@AndreasDietrich-次の解決策は常に正しいようです:regexp_replace(your_string, '([^,]+)(,\1)*(,|$)', '\1\3')
Egor Skriptunoff

10

文書化されていないwm_concat関数を使用できます。

select col1, wm_concat(distinct col2) col2_list 
from tab1
group by col1;

この関数は、dbms_lob.substrclobをvarchar2に変換する場合に使用できるclob列を返します。


15
いいえ、使用しないでください。
コシナエ2014年

1
これはまさに私が必要とするものであり、既存の集約されたクエリ内で完全に機能しました。使用の何が問題になっていwm_concat(distinct x)ますか?
Ehryk 2015

1
文書化されておらず、12cにも存在しないため。とにかく古いバージョンでは、それが最善の方法だと思います。
KemalettinErbakırcı15年

1
ありがとう@kemalettinerbakırcı!@thgあなたは何かが文書化されていない場合、あなたはそれが副作用であるかわからないこと、および文書化が文書化された関数について教えてくれる他の種類のことを考慮する必要があります。あなたはそれをブラックボックスとして使用するだけで、どのレバーが民間伝承に基づいて何をするかを知っているだけです。
Koshinae

4
決して使用しないでくださいwm_concatOracleでWM_CONCAT関数を使用しない理由を参照してください
Lalit Kumar B

7

最初に値でグループ化し、次にlistaggで別の集計を行うことで、この問題を解決しました。このようなもの:

select a,b,listagg(c,',') within group(order by c) c, avg(d)
from (select a,b,c,avg(d)
      from   table
      group by (a,b,c))
group by (a,b)

1つのフルテーブルアクセスのみ、比較的複雑なクエリへの拡張が比較的容易


6

この変換を複数の列に適用することを意図している場合は、a_horse_with_no_nameのソリューションを拡張しました。

SELECT * FROM
(SELECT LISTAGG(GRADE_LEVEL, ',') within group(order by GRADE_LEVEL) "Grade Levels" FROM (select distinct GRADE_LEVEL FROM Students) t)                     t1,
(SELECT LISTAGG(ENROLL_STATUS, ',') within group(order by ENROLL_STATUS) "Enrollment Status" FROM (select distinct ENROLL_STATUS FROM Students) t)          t2,
(SELECT LISTAGG(GENDER, ',') within group(order by GENDER) "Legal Gender Code" FROM (select distinct GENDER FROM Students) t)                               t3,
(SELECT LISTAGG(CITY, ',') within group(order by CITY) "City" FROM (select distinct CITY FROM Students) t)                                                  t4,
(SELECT LISTAGG(ENTRYCODE, ',') within group(order by ENTRYCODE) "Entry Code" FROM (select distinct ENTRYCODE FROM Students) t)                             t5,
(SELECT LISTAGG(EXITCODE, ',') within group(order by EXITCODE) "Exit Code" FROM (select distinct EXITCODE FROM Students) t)                                 t6,
(SELECT LISTAGG(LUNCHSTATUS, ',') within group(order by LUNCHSTATUS) "Lunch Status" FROM (select distinct LUNCHSTATUS FROM Students) t)                     t7,
(SELECT LISTAGG(ETHNICITY, ',') within group(order by ETHNICITY) "Race Code" FROM (select distinct ETHNICITY FROM Students) t)                              t8,
(SELECT LISTAGG(CLASSOF, ',') within group(order by CLASSOF) "Expected Graduation Year" FROM (select distinct CLASSOF FROM Students) t)                     t9,
(SELECT LISTAGG(TRACK, ',') within group(order by TRACK) "Track Code" FROM (select distinct TRACK FROM Students) t)                                         t10,
(SELECT LISTAGG(GRADREQSETID, ',') within group(order by GRADREQSETID) "Graduation ID" FROM (select distinct GRADREQSETID FROM Students) t)                 t11,
(SELECT LISTAGG(ENROLLMENT_SCHOOLID, ',') within group(order by ENROLLMENT_SCHOOLID) "School Key" FROM (select distinct ENROLLMENT_SCHOOLID FROM Students) t)       t12,
(SELECT LISTAGG(FEDETHNICITY, ',') within group(order by FEDETHNICITY) "Federal Race Code" FROM (select distinct FEDETHNICITY FROM Students) t)                         t13,
(SELECT LISTAGG(SUMMERSCHOOLID, ',') within group(order by SUMMERSCHOOLID) "Summer School Key" FROM (select distinct SUMMERSCHOOLID FROM Students) t)                               t14,
(SELECT LISTAGG(FEDRACEDECLINE, ',') within group(order by FEDRACEDECLINE) "Student Decl to Prov Race Code" FROM (select distinct FEDRACEDECLINE FROM Students) t)          t15

これは、Oracle Database 11g Enterprise Editionリリース11.2.0.2.0-64ビット本番です。
DISTINCTとORDERを行う方法がないため、STRAGGを使用できませんでした。

対象となるすべての列を追加しているため、パフォーマンスは直線的に向上します。77K行の場合、上記は3秒かかりました。たった1つのロールアップの場合、.172秒。1つのパスでテーブルの複数の列を区別する方法がありました。


6

複数の列にまたがる個別の値が必要な場合、並べ替え順序の制御が必要な場合、文書化されていない可能性のある非公開の関数を使用したくない場合、および複数の全テーブルスキャンが不要な場合、次の構文が便利です。

with test_data as 
(
      select 'A' as col1, 'T_a1' as col2, '123' as col3 from dual
union select 'A', 'T_a1', '456' from dual
union select 'A', 'T_a1', '789' from dual
union select 'A', 'T_a2', '123' from dual
union select 'A', 'T_a2', '456' from dual
union select 'A', 'T_a2', '111' from dual
union select 'A', 'T_a3', '999' from dual
union select 'B', 'T_a1', '123' from dual
union select 'B', 'T_b1', '740' from dual
union select 'B', 'T_b1', '846' from dual
)
select col1
     , (select listagg(column_value, ',') within group (order by column_value desc) from table(collect_col2)) as col2s
     , (select listagg(column_value, ',') within group (order by column_value desc) from table(collect_col3)) as col3s
from 
(
select col1
     , collect(distinct col2) as collect_col2
     , collect(distinct col3) as collect_col3
from test_data
group by col1
);

1
「union」を「union all」に置き換えると、さらに時間を節約できる場合があります。
バーカイ

4

「明確な」部分を作る専用の関数を作成するのはどうですか?

create or replace function listagg_distinct (t in str_t, sep IN VARCHAR2 DEFAULT ',') 
  return VARCHAR2
as 
  l_rc VARCHAR2(4096) := '';
begin
  SELECT listagg(val, sep) WITHIN GROUP (ORDER BY 1)
    INTO l_rc
    FROM (SELECT DISTINCT column_value val FROM table(t));
  RETURN l_rc;
end;
/

そして、それを使用して集計を行います:

SELECT col1, listagg_distinct(cast(collect(col_2) as str_t ), ', ')
  FROM your_table
  GROUP BY col_1;

4

文字列の長さの問題を回避するXMLAGGには、似てlistaggいますが、CLOBを返します。

次に、を使用regexp_replaceして解析して一意の値を取得し、を使用して文字列に戻すことができますdbms_lob.substr()。個別の値が大量にある場合でも、この方法ではスペースが不足しますが、多くの場合、以下のコードが機能します。

使用する区切り文字を変更することもできます。私の場合、「-」ではなく「-」が必要でしたが、コード内のダッシュを置き換えて、必要に応じてコンマを使用できるはずです。

select col1,
    dbms_lob.substr(ltrim(REGEXP_REPLACE(REPLACE(
         REPLACE(
           XMLAGG(
             XMLELEMENT("A",col2)
               ORDER BY col2).getClobVal(),
             '<A>','-'),
             '</A>',''),'([^-]*)(-\1)+($|-)', 
           '\1\3'),'-'), 4000,1) as platform_mix
from table

これは、dbms_xmlgen.convert(string、1)を呼び出して変換を削除して&->&amp変換する必要があるという優れたアイデアです。私の投稿リンクを
ozmike

3

DECODE対CASEを使用した@a_horse_with_no_nameのrow_number()ベースのアプローチに対する@YoYoの修正をさらに洗練します(ここで見ました)。@Martin Vrbovskyにもこのケースアプローチの回答があることがわかります。

select
  col1, 
  listagg(col2, ',') within group (order by col2) AS col2_list,
  listagg(col3, ',') within group (order by col3) AS col3_list,
  SUM(col4) AS col4
from (
  select
    col1, 
    decode(row_number() over (partition by col1, col2 order by null),1,col2) as col2,
    decode(row_number() over (partition by col1, col3 order by null),1,col3) as col3
  from foo
)
group by col1;

2

次期Oracle 19cはでサポートさDISTINCTLISTAGGます。

DISTINCTオプションを指定したLISTAGG

この機能は19cに付属しています。

SQL> select deptno, listagg (distinct sal,', ') within group (order by sal)  
  2  from scott.emp  
  3  group by deptno;  

編集:

Oracle 19C LISTAGG DISTINCT

LISTAGG集約関数は、新しいDISTINCTキーワードを使用して重複排除をサポートするようになりました。LISTAGG集約関数は、ORDER BY式に従ってクエリ内の各グループの行を並べ替え、値を単一の文字列に連結します。新しいDISTINCTキーワードを使用すると、単一の文字列に連結する前に、指定された式から重複する値を削除できます。これにより、集約LISTAGG関数を使用する前に、複雑なクエリ処理を作成して個別の値を見つける必要がなくなります。DISTINCTオプションを使用すると、重複する値を削除する処理をLISTAGG関数内で直接実行できます。その結果、SQLはよりシンプルで、より速く、より効率的になります。


0

PARTITION BY句の使用を考えている人はいますか?このクエリでは、アプリケーションサービスとアクセスのリストを取得するのに役立ちました。

SELECT DISTINCT T.APP_SVC_ID, 
       LISTAGG(RTRIM(T.ACCESS_MODE), ',') WITHIN GROUP(ORDER BY T.ACCESS_MODE) OVER(PARTITION BY T.APP_SVC_ID) AS ACCESS_MODE 
  FROM APP_SVC_ACCESS_CNTL T 
 GROUP BY T.ACCESS_MODE, T.APP_SVC_ID

私はNDAのwhere句を切り取らなければなりませんでしたが、あなたはそのアイデアを理解しました。


このクエリでの個別のアイテムを取得する方法がわかりませんLISTAGGT.ACCESS_MODEグループ化しているため、行ごとに1 つしかないようです。
jpmc26 2017年

0

私はこれが役立つと思います-重複している場合は列の値をNULLにケースします-それからLISTAGG文字列に追加されません:

with test_data as 
(
      select 1 as col1, 2 as col2, 'Smith' as created_by from dual
union select 1, 2, 'John' from dual
union select 1, 3, 'Ajay' from dual
union select 1, 4, 'Ram' from dual
union select 1, 5, 'Jack' from dual
union select 2, 5, 'Smith' from dual
union select 2, 6, 'John' from dual
union select 2, 6, 'Ajay' from dual
union select 2, 6, 'Ram' from dual
union select 2, 7, 'Jack' from dual
)
SELECT col1  ,
      listagg(col2 , ',') within group (order by col2 ASC) AS orig_value,
      listagg(CASE WHEN rwn=1 THEN col2 END , ',') within group (order by col2 ASC) AS distinct_value
from 
    (
    select row_number() over (partition by col1,col2 order by 1) as rwn, 
           a.*
    from test_data a
    ) a
GROUP BY col1   

結果:

COL1  ORIG         DISTINCT
1   2,2,3,4,5   2,3,4,5
2   5,6,6,6,7   5,6,7

0

listagg()はNULL値を無視するため、最初のステップでlag()関数を使用して、前のレコードに同じ値があったかどうかを分析できます(ある場合はNULL、そうでない場合は「新しい値」)。

WITH tab AS 
(           
          SELECT 1 as col1, 2 as col2, 'Smith' as created_by FROM dual
UNION ALL SELECT 1 as col1, 2 as col2, 'John'  as created_by FROM dual
UNION ALL SELECT 1 as col1, 3 as col2, 'Ajay'  as created_by FROM dual
UNION ALL SELECT 1 as col1, 4 as col2, 'Ram'   as created_by FROM dual
UNION ALL SELECT 1 as col1, 5 as col2, 'Jack'  as created_by FROM dual
)
SELECT col1
     , CASE 
       WHEN lag(col2) OVER (ORDER BY col2) = col2 THEN 
         NULL 
       ELSE 
         col2 
       END as col2_with_nulls
     , created_by
  FROM tab;

結果

      COL1 COL2_WITH_NULLS CREAT
---------- --------------- -----
         1               2 Smith
         1                 John
         1               3 Ajay
         1               4 Ram
         1               5 Jack

2番目の2はNULLに置き換えられることに注意してください。これで、SELECTをlistagg()でラップできます。

WITH tab AS 
(           
          SELECT 1 as col1, 2 as col2, 'Smith' as created_by FROM dual
UNION ALL SELECT 1 as col1, 2 as col2, 'John'  as created_by FROM dual
UNION ALL SELECT 1 as col1, 3 as col2, 'Ajay'  as created_by FROM dual
UNION ALL SELECT 1 as col1, 4 as col2, 'Ram'   as created_by FROM dual
UNION ALL SELECT 1 as col1, 5 as col2, 'Jack'  as created_by FROM dual
)
SELECT listagg(col2_with_nulls, ',') WITHIN GROUP (ORDER BY col2_with_nulls) col2_list
  FROM ( SELECT col1
              , CASE WHEN lag(col2) OVER (ORDER BY col2) = col2 THEN NULL ELSE col2 END as col2_with_nulls
              , created_by
           FROM tab );

結果

COL2_LIST
---------
2,3,4,5

複数の列に対してもこれを行うことができます。

WITH tab AS 
(           
          SELECT 1 as col1, 2 as col2, 'Smith' as created_by FROM dual
UNION ALL SELECT 1 as col1, 2 as col2, 'John'  as created_by FROM dual
UNION ALL SELECT 1 as col1, 3 as col2, 'Ajay'  as created_by FROM dual
UNION ALL SELECT 1 as col1, 4 as col2, 'Ram'   as created_by FROM dual
UNION ALL SELECT 1 as col1, 5 as col2, 'Jack'  as created_by FROM dual
)
SELECT listagg(col1_with_nulls, ',') WITHIN GROUP (ORDER BY col1_with_nulls) col1_list
     , listagg(col2_with_nulls, ',') WITHIN GROUP (ORDER BY col2_with_nulls) col2_list
     , listagg(created_by, ',')      WITHIN GROUP (ORDER BY created_by) created_by_list
  FROM ( SELECT CASE WHEN lag(col1) OVER (ORDER BY col1) = col1 THEN NULL ELSE col1 END as col1_with_nulls
              , CASE WHEN lag(col2) OVER (ORDER BY col2) = col2 THEN NULL ELSE col2 END as col2_with_nulls
              , created_by
           FROM tab );

結果

COL1_LIST COL2_LIST CREATED_BY_LIST
--------- --------- -------------------------
1         2,3,4,5   Ajay,Jack,John,Ram,Smith

0

あなたはRegExの交換を介してそれを行うことができます。次に例を示します。

-- Citations Per Year - Cited Publications main query. Includes list of unique associated core project numbers, ordered by core project number.
SELECT ptc.pmid AS pmid, ptc.pmc_id, ptc.pub_title AS pubtitle, ptc.author_list AS authorlist,
  ptc.pub_date AS pubdate,
  REGEXP_REPLACE( LISTAGG ( ppcc.admin_phs_org_code || 
    TO_CHAR(ppcc.serial_num,'FM000000'), ',') WITHIN GROUP (ORDER BY ppcc.admin_phs_org_code || 
    TO_CHAR(ppcc.serial_num,'FM000000')),
    '(^|,)(.+)(,\2)+', '\1\2')
  AS projectNum
FROM publication_total_citations ptc
  JOIN proj_paper_citation_counts ppcc
    ON ptc.pmid = ppcc.pmid
   AND ppcc.citation_year = 2013
  JOIN user_appls ua
    ON ppcc.admin_phs_org_code = ua.admin_phs_org_code
   AND ppcc.serial_num = ua.serial_num
   AND ua.login_id = 'EVANSF'
GROUP BY ptc.pmid, ptc.pmc_id, ptc.pub_title, ptc.author_list, ptc.pub_date
ORDER BY pmid;

ここにも投稿:Oracle-固有のListagg値


0

次のように作成されたlistagg_clob関数を使用します。

create or replace package list_const_p
is
list_sep varchar2(10) := ',';
end list_const_p;
/
sho err

create type listagg_clob_t as object(
v_liststring varchar2(32767),
v_clob clob,
v_templob number,

static function ODCIAggregateInitialize(
sctx IN OUT listagg_clob_t
) return number,
member function ODCIAggregateIterate(
self IN OUT listagg_clob_t, value IN varchar2
) return number,
member function ODCIAggregateTerminate(
self IN OUT listagg_clob_t, returnValue OUT clob, flags IN number
) return number,
member function ODCIAggregateMerge(
self IN OUT listagg_clob_t, ctx2 IN OUT listagg_clob_t
) return number
);
/
sho err

create or replace type body listagg_clob_t is

static function ODCIAggregateInitialize(sctx IN OUT listagg_clob_t)
return number is
begin
sctx := listagg_clob_t('', '', 0);
return ODCIConst.Success;
end;

member function ODCIAggregateIterate(
self IN OUT listagg_clob_t,
value IN varchar2
) return number is
begin
if nvl(lengthb(v_liststring),0) + nvl(lengthb(value),0) <= 4000 then
self.v_liststring:=self.v_liststring || value || list_const_p.list_sep;
else
if self.v_templob = 0 then
dbms_lob.createtemporary(self.v_clob, true, dbms_lob.call);
self.v_templob := 1;
end if;
dbms_lob.writeappend(self.v_clob, length(self.v_liststring), v_liststring);
self.v_liststring := value || list_const_p.list_sep;
end if;
return ODCIConst.Success;
end;

member function ODCIAggregateTerminate(
self IN OUT listagg_clob_t,
returnValue OUT clob,
flags IN number
) return number is
begin
if self.v_templob != 0 then
dbms_lob.writeappend(self.v_clob, length(self.v_liststring), self.v_liststring);
dbms_lob.trim(self.v_clob, dbms_lob.getlength(self.v_clob) - 1);
else
self.v_clob := substr(self.v_liststring, 1, length(self.v_liststring) - 1);
end if;
returnValue := self.v_clob;
return ODCIConst.Success;
end;

member function ODCIAggregateMerge(self IN OUT listagg_clob_t, ctx2 IN OUT listagg_clob_t) return number is
begin
if ctx2.v_templob != 0 then
if self.v_templob != 0 then
dbms_lob.append(self.v_clob, ctx2.v_clob);
dbms_lob.freetemporary(ctx2.v_clob);
ctx2.v_templob := 0;
else
self.v_clob := ctx2.v_clob;
self.v_templob := 1;
ctx2.v_clob := '';
ctx2.v_templob := 0;
end if;
end if;
if nvl(lengthb(self.v_liststring),0) + nvl(lengthb(ctx2.v_liststring),0) <= 4000 then
self.v_liststring := self.v_liststring || ctx2.v_liststring;
ctx2.v_liststring := '';
else
if self.v_templob = 0 then
dbms_lob.createtemporary(self.v_clob, true, dbms_lob.call);
self.v_templob := 1;
end if;
dbms_lob.writeappend(self.v_clob, length(self.v_liststring), self.v_liststring);
dbms_lob.writeappend(self.v_clob, length(ctx2.v_liststring), ctx2.v_liststring);
self.v_liststring := '';
ctx2.v_liststring := '';
end if;
return ODCIConst.Success;
end;
end;
/
sho err

CREATE or replace FUNCTION listagg_clob (input varchar2) RETURN clob
PARALLEL_ENABLE AGGREGATE USING listagg_clob_t;
/
sho err 


0

正規表現を使ってこれを処理する関数を書きました。inパラメータは次のとおりです。1)listagg自体を呼び出す2)区切り文字の繰り返し

create or replace function distinct_listagg
  (listagg_in varchar2,
   delimiter_in varchar2)

   return varchar2
   as
   hold_result varchar2(4000);
   begin

   select rtrim( regexp_replace( (listagg_in)
      , '([^'||delimiter_in||']*)('||
      delimiter_in||'\1)+($|'||delimiter_in||')', '\1\3'), ',')
      into hold_result
      from dual;

return hold_result;

end;

これで、これを行うたびに正規表現を繰り返す必要がなくなり、次のように言うだけです。

select distinct_listagg(
                       listagg(myfield,', ') within group (order by 1),
                       ', '
                       )
     from mytable;

0

連結された値の特定の順序が必要なく、区切り文字がコンマである場合は、次のようにすることができます。

select col1, stragg(distinct col2)
  from table
 group by col1

0

私はこれのDISTINCTバージョンを必要とし、これを機能させました。

RTRIM(REGEXP_REPLACE(
                       (value, ', ') WITHIN GROUP( ORDER BY value)), 
                            '([^ ]+)(, \1)+','\1'),', ') 

0

厄介な点の1つLISTAGGは、連結された文字列の合計長が4000文字(VARCHAR2SQLの場合の制限)を超えると、以下のエラーがスローされ、Oracleバージョン12.1まででは管理が難しいことです。

ORA-01489:文字列連結の結果が長すぎます

12cR2で追加された新機能は、のON OVERFLOW句ですLISTAGG。この句を含むクエリは次のようになります。

SELECT pid, LISTAGG(Desc, ' ' on overflow truncate) WITHIN GROUP (ORDER BY seq) AS desc
FROM B GROUP BY pid;

上記は出力を4000文字に制限しますが、ORA-01489エラーをスローしません 。

これらは、ON OVERFLOW節の追加オプションの一部です。

  • ON OVERFLOW TRUNCATE 'Contd..' :これは'Contd..'文字列の最後に表示されます(デフォルトは...
  • ON OVERFLOW TRUNCATE '' :これにより、終了文字列なしで4000文字が表示されます。
  • ON OVERFLOW TRUNCATE WITH COUNT:これは、終了文字の後の最後の合計文字数を表示します。例:-' ...(5512)'
  • ON OVERFLOW ERRORLISTAGGORA-01489エラーで失敗すると 予想される場合(これはいずれにしてもデフォルトです)。

0

私はこのストアド関数を実装しました:

CREATE TYPE LISTAGG_DISTINCT_PARAMS AS OBJECT (ELEMENTO VARCHAR2(2000), SEPARATORE VARCHAR2(10));

CREATE TYPE T_LISTA_ELEMENTI AS TABLE OF VARCHAR2(2000);

CREATE TYPE T_LISTAGG_DISTINCT AS OBJECT (

    LISTA_ELEMENTI T_LISTA_ELEMENTI,
        SEPARATORE VARCHAR2(10),

    STATIC FUNCTION ODCIAGGREGATEINITIALIZE(SCTX  IN OUT            T_LISTAGG_DISTINCT) 
                    RETURN NUMBER,

    MEMBER FUNCTION ODCIAGGREGATEITERATE   (SELF  IN OUT            T_LISTAGG_DISTINCT, 
                                            VALUE IN                    LISTAGG_DISTINCT_PARAMS ) 
                    RETURN NUMBER,

    MEMBER FUNCTION ODCIAGGREGATETERMINATE (SELF         IN     T_LISTAGG_DISTINCT,
                                            RETURN_VALUE OUT    VARCHAR2, 
                                            FLAGS        IN     NUMBER      )
                    RETURN NUMBER,

    MEMBER FUNCTION ODCIAGGREGATEMERGE       (SELF               IN OUT T_LISTAGG_DISTINCT,
                                                                                        CTX2                 IN         T_LISTAGG_DISTINCT    )
                    RETURN NUMBER
);

CREATE OR REPLACE TYPE BODY T_LISTAGG_DISTINCT IS 

    STATIC FUNCTION ODCIAGGREGATEINITIALIZE(SCTX IN OUT T_LISTAGG_DISTINCT) RETURN NUMBER IS 
    BEGIN
                SCTX := T_LISTAGG_DISTINCT(T_LISTA_ELEMENTI() , ',');
        RETURN ODCICONST.SUCCESS;
    END;

    MEMBER FUNCTION ODCIAGGREGATEITERATE(SELF IN OUT T_LISTAGG_DISTINCT, VALUE IN LISTAGG_DISTINCT_PARAMS) RETURN NUMBER IS
    BEGIN

                IF VALUE.ELEMENTO IS NOT NULL THEN
                        SELF.LISTA_ELEMENTI.EXTEND;
                        SELF.LISTA_ELEMENTI(SELF.LISTA_ELEMENTI.LAST) := TO_CHAR(VALUE.ELEMENTO);
                        SELF.LISTA_ELEMENTI:= SELF.LISTA_ELEMENTI MULTISET UNION DISTINCT SELF.LISTA_ELEMENTI;
                        SELF.SEPARATORE := VALUE.SEPARATORE;
                END IF;
        RETURN ODCICONST.SUCCESS;
    END;

    MEMBER FUNCTION ODCIAGGREGATETERMINATE(SELF IN T_LISTAGG_DISTINCT, RETURN_VALUE OUT VARCHAR2, FLAGS IN NUMBER) RETURN NUMBER IS
      STRINGA_OUTPUT            CLOB:='';
            LISTA_OUTPUT                T_LISTA_ELEMENTI;
            TERMINATORE                 VARCHAR2(3):='...';
            LUNGHEZZA_MAX           NUMBER:=4000;
    BEGIN

                IF SELF.LISTA_ELEMENTI.EXISTS(1) THEN -- se esiste almeno un elemento nella lista

                        -- inizializza una nuova lista di appoggio
                        LISTA_OUTPUT := T_LISTA_ELEMENTI();

                        -- riversamento dei soli elementi in DISTINCT
                        LISTA_OUTPUT := SELF.LISTA_ELEMENTI MULTISET UNION DISTINCT SELF.LISTA_ELEMENTI;

                        -- ordinamento degli elementi
                        SELECT CAST(MULTISET(SELECT * FROM TABLE(LISTA_OUTPUT) ORDER BY 1 ) AS T_LISTA_ELEMENTI ) INTO LISTA_OUTPUT FROM DUAL;

                        -- concatenazione in una stringa                        
                        FOR I IN LISTA_OUTPUT.FIRST .. LISTA_OUTPUT.LAST - 1
                        LOOP
                            STRINGA_OUTPUT := STRINGA_OUTPUT || LISTA_OUTPUT(I) || SELF.SEPARATORE;
                        END LOOP;
                        STRINGA_OUTPUT := STRINGA_OUTPUT || LISTA_OUTPUT(LISTA_OUTPUT.LAST);

                        -- se la stringa supera la dimensione massima impostata, tronca e termina con un terminatore
                        IF LENGTH(STRINGA_OUTPUT) > LUNGHEZZA_MAX THEN
                                    RETURN_VALUE := SUBSTR(STRINGA_OUTPUT, 0, LUNGHEZZA_MAX - LENGTH(TERMINATORE)) || TERMINATORE;
                        ELSE
                                    RETURN_VALUE:=STRINGA_OUTPUT;
                        END IF;

                ELSE -- se non esiste nessun elemento, restituisci NULL

                        RETURN_VALUE := NULL;

                END IF;

        RETURN ODCICONST.SUCCESS;
    END;

    MEMBER FUNCTION ODCIAGGREGATEMERGE(SELF IN OUT T_LISTAGG_DISTINCT, CTX2 IN T_LISTAGG_DISTINCT) RETURN NUMBER IS
    BEGIN
        RETURN ODCICONST.SUCCESS;
    END;

END; -- fine corpo

CREATE
FUNCTION LISTAGG_DISTINCT (INPUT LISTAGG_DISTINCT_PARAMS) RETURN VARCHAR2
    PARALLEL_ENABLE AGGREGATE USING T_LISTAGG_DISTINCT;

// Example
SELECT LISTAGG_DISTINCT(LISTAGG_DISTINCT_PARAMS(OWNER, ', ')) AS LISTA_OWNER
FROM SYS.ALL_OBJECTS;

申し訳ありませんが、場合によっては(非常に大きなセットの場合)、Oracleから次のエラーが返されることがあります。

Object or Collection value was too large. The size of the value
might have exceeded 30k in a SORT context, or the size might be
too big for available memory.

しかし、これは良い出発点だと思います;)


0

select col1, listaggr(col2,',') within group(Order by col2) from table group by col1 文字列(col2)をリストに集約して順序nを維持し、その後、重複をグループ1としてグループ1として処理します。これは、1つのグループにcol1の重複をマージすることを意味します。おそらくこれはクリーンでシンプルなように見え、col3も必要な場合は、listagg()を1つ追加する必要があります。select col1, listaggr(col2,',') within group(Order by col2),listaggr(col3,',') within group(order by col3) from table group by col1


0

SELECT DISTINCT ...LISTAGGを呼び出す前にサブクエリの一部として使用することは、@ a_horse_with_no_nameで指摘されているように、おそらく単純なクエリの最良の方法です。

ただし、より複雑なクエリでは、これを実現することは不可能または簡単ではありません。これは、分析関数を使用したトップnアプローチを使用していたシナリオで発生しました。

そこで、COLLECT集計関数を見つけました。UNIQUEor DISTINCT修飾子を使用できるように記載されています。10gでのみ静かに失敗します(エラーなしで修飾子を無視します)。しかし、これを克服するために、別の答えから、私はこの解決策に行きました:

SELECT
  ...
  (
    SELECT LISTAGG(v.column_value,',') WITHIN GROUP (ORDER BY v.column_value)
    FROM TABLE(columns_tab) v
  ) AS columns,
  ...
FROM (
  SELECT
    ...
    SET(CAST(COLLECT(UNIQUE some_column ORDER BY some_column) AS tab_typ)) AS columns_tab,
    ...
)

基本的に、を使用SETして、コレクション内の重複を削除します。

それでもtab_typ、を基本的なコレクション型として定義する必要があります。の場合VARCHAR、これはたとえば次のようになります。

CREATE OR REPLACE type tab_typ as table of varchar2(100)
/

また、3列目(またはそれ以上)の列でまだ集計したい場合がある、複数列の状況での@a_horse_with_no_nameからの回答の修正として:

select
  col1, 
  listagg(CASE rn2 WHEN 1 THEN col2 END, ',') within group (order by col2) AS col2_list,
  listagg(CASE rn3 WHEN 1 THEN col3 END, ',') within group (order by col3) AS col3_list,
  SUM(col4) AS col4
from (
  select
    col1, 
    col2,
    row_number() over (partition by col1, col2 order by null) as rn2,
    row_number() over (partition by col1, col3 order by null) as rn3
  from foo
)
group by col1;

rn = 1where条件をクエリに残しておくと、他の列が正しく集計されなくなります。


0

非常にシンプル-クエリで、select differentを使用したサブクエリを使用します。

SELECT question_id,
       LISTAGG(element_id, ',') WITHIN GROUP (ORDER BY element_id)
FROM
       (SELECT distinct question_id, element_id
       FROM YOUR_TABLE)
GROUP BY question_id;

-1

複数のlistaggを処理する最も簡単な方法は、select differentからのその列のlistaggを含む列ごとに1つのWITH(サブクエリ係数)を使用することです。

    WITH tab AS 
    (           
        SELECT 1 as col1, 2 as col2, 3 as col3, 'Smith' as created_by FROM dual
        UNION ALL SELECT 1 as col1, 2 as col2, 3 as col3,'John'  as created_by FROM dual
        UNION ALL SELECT 1 as col1, 3 as col2, 4 as col3,'Ajay'  as created_by FROM dual
        UNION ALL SELECT 1 as col1, 4 as col2, 4 as col3,'Ram'   as created_by FROM dual
        UNION ALL SELECT 1 as col1, 5 as col2, 6 as col3,'Jack'  as created_by FROM dual
    )
    , getCol2 AS
    (
        SELECT  DISTINCT col1, listagg(col2,',') within group (order by col2)  over (partition by col1) AS col2List
        FROM ( SELECT DISTINCT col1,col2 FROM tab)
    )
    , getCol3 AS
    (
        SELECT  DISTINCT col1, listagg(col3,',') within group (order by col3)  over (partition by col1) AS col3List
        FROM ( SELECT DISTINCT col1,col3 FROM tab)
    )
    select col1,col2List,col3List
    FROM getCol2
    JOIN getCol3
    using (col1)

それは与える:

col1  col2List  col3List
1     2,3,4,5   3,4,6
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.