web-dev-qa-db-ja.com

Oracleの複数行からの列値を連結するためのSQLクエリ

複数の行の列値を連結するようにSQLを構成することは可能でしょうか。

以下はその例です。

表A

[PID] A [A] B [C] C [C]

表B

[。] PID SEQ Desc [。] [1] A 1 A [2] A [3] A [3] A [1] B 1 [1]いい仕事[1] C 1はい[C 2]できます[C 2] C 3 C [3]できます[C 4]この作業![A]

SQLの出力は以下のようになります -

 PID Desc 
 A良い一日を過ごします。
 Bいい仕事をします。
 Cはい、この仕事をすることができます!

したがって、基本的に出力テーブルのDesc列はテーブルBのSEQ値を連結したものですか。

SQLに関するヘルプ

141
jagamot

使用しているバージョンに応じていくつかの方法があります。 文字列集計手法に関するOracleのドキュメント を参照してください。非常に一般的なのは LISTAGG を使うことです。

SELECT pid, LISTAGG(Desc, ' ') WITHIN GROUP (ORDER BY seq) AS description
FROM B GROUP BY pid;

それからAに参加して、必要なpidsを選択します。

注:出荷時の状態で、LISTAGGVARCHAR2列でのみ正しく機能します。

198
Lou Franco

11.2より前のバージョンで動作するXMLAGG関数もあります。 WM_CONCATドキュメントに記載されていない、Oracleではサポートされていない なので、本番システムでは使用しないことをお勧めします。

XMLAGGを使用すると、次のことができます。

SELECT XMLAGG(XMLELEMENT(E,ename||',')).EXTRACT('//text()') "Result" 
FROM employee_names

これは何ですか

  • employee_namesテーブルのename列(コンマで連結)の値を(タグEを持つ)xml要素に入れます。
  • これのテキストを抽出する
  • xMLを集約する(連結する)
  • 結果列を「結果」と呼びます。
19
Peter

SQLモデル節の場合:

SQL> select pid
  2       , ltrim(sentence) sentence
  3    from ( select pid
  4                , seq
  5                , sentence
  6             from b
  7            model
  8                  partition by (pid)
  9                  dimension by (seq)
 10                  measures (descr,cast(null as varchar2(100)) as sentence)
 11                  ( sentence[any] order by seq desc
 12                    = descr[cv()] || ' ' || sentence[cv()+1]
 13                  )
 14         )
 15   where seq = 1
 16  /

P SENTENCE
- ---------------------------------------------------------------------------
A Have a Nice day
B Nice Work.
C Yes we can do this work!

3 rows selected.

私はこれについて書きました ここ 。また、OTNスレッドへのリンクをたどると、パフォーマンスの比較を含めて、もう少しわかります。

11
Rob van Wijk

LISTAGG分析関数は、Oracle 11g Release 2で導入されました、文字列の集計が非常に簡単になります。 11g Release 2を使用している場合は、文字列集計にこの関数を使用する必要があります。文字列連結の詳細については、以下のURLを参照してください。

http://www.Oracle-base.com/articles/misc/StringAggregationTechniques.php

文字列連結

8
Ashish J

ほとんどの回答が示唆しているように、LISTAGGは明らかな選択肢です。ただし、LISTAGGの面倒な点の1つは、連結された文字列の合計長が4000文字を超えると(SQLのVARCHAR2の制限)、次のエラーが発生することです。これは、Oracleバージョン12.1まででは管理が困難です。

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

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

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エラーで失敗すると予想される場合(とにかくデフォルトです)。
7
Kaushik Nayak

Oracle 9i(またはそれ以前)を使用してこの問題を解決しなければならない場合は、LISTAGGが使用できないため、おそらくSYS_CONNECT_BY_PATHを使用する必要があります。

OPに答えるために、次のクエリはテーブルAのPIDを表示し、テーブルBのすべてのDESCカラムを連結します。

SELECT pid, SUBSTR (MAX (SYS_CONNECT_BY_PATH (description, ', ')), 3) all_descriptions
FROM (
       SELECT ROW_NUMBER () OVER (PARTITION BY pid ORDER BY pid, seq) rnum, pid, description
       FROM (
              SELECT a.pid, seq, description
              FROM table_a a, table_b b
              WHERE a.pid = b.pid(+)
             )
      )
START WITH rnum = 1
CONNECT BY PRIOR rnum = rnum - 1 AND PRIOR pid = pid
GROUP BY pid
ORDER BY pid;

キーと値がすべて1つのテーブルに含まれている場合もあります。次のクエリは、テーブルAがなく、テーブルBだけが存在する場合に使用できます。

SELECT pid, SUBSTR (MAX (SYS_CONNECT_BY_PATH (description, ', ')), 3) all_descriptions
FROM (
       SELECT ROW_NUMBER () OVER (PARTITION BY pid ORDER BY pid, seq) rnum, pid, description
       FROM (
              SELECT pid, seq, description
              FROM table_b
             )
      )
START WITH rnum = 1
CONNECT BY PRIOR rnum = rnum - 1 AND PRIOR pid = pid
GROUP BY pid
ORDER BY pid;

すべての値は必要に応じて並べ替えることができます。個々の連結された記述はPARTITION BY句で並べ替えることができ、PIDのリストは最後のORDER BY句で並べ替えることができます。


代替方法:テーブル全体のすべての値を1行に連結したい場合があります。

ここでの重要な考え方は、連結する記述のグループに人為的な値を使用することです。

次のクエリでは、定数文字列 '1'が使用されていますが、どの値でも機能します。

SELECT SUBSTR (MAX (SYS_CONNECT_BY_PATH (description, ', ')), 3) all_descriptions
FROM (
       SELECT ROW_NUMBER () OVER (PARTITION BY unique_id ORDER BY pid, seq) rnum, description
       FROM (
              SELECT '1' unique_id, b.pid, b.seq, b.description
              FROM table_b b
             )
      )
START WITH rnum = 1
CONNECT BY PRIOR rnum = rnum - 1;

個々の連結記述は、PARTITION BY句で並べ替えることができます。

このページの他のいくつかの回答でも、この非常に参考になった参照について言及しています。 https://Oracle-base.com/articles/misc/string-aggregation-techniques

5
  1. ソートが必須の場合、LISTAGGは最高のパフォーマンスを発揮します(00:00:05.85)

    SELECT pid, LISTAGG(Desc, ' ') WITHIN GROUP (ORDER BY seq) AS description FROM B GROUP BY pid;

  2. ソートが不要な場合(00:00:02.90)、COLLECTは最高のパフォーマンスを発揮します。

    SELECT pid, TO_STRING(CAST(COLLECT(Desc) AS varchar2_ntt)) AS Vals FROM B GROUP BY pid;

  3. 順序付きCOLLECTは少し遅くなります(00:00:07.08)。

    SELECT pid, TO_STRING(CAST(COLLECT(Desc ORDER BY Desc) AS varchar2_ntt)) AS Vals FROM B GROUP BY pid;

他のすべての手法は遅くなりました。

2
Misho

選択クエリを実行する前に、これを実行します。

SET SERVEROUT ON SIZE 6000

SELECT XMLAGG(XMLELEMENT(E,SUPLR_SUPLR_ID||',')).EXTRACT('//text()') "SUPPLIER" 
FROM SUPPLIERS;
1
user2865810

LISTAGGを使用していますが、この文字列をペルシャ文字列として返します。

私の質問:

SELECT
 listagg(DESCRIPTION,' , ') within group (order by DESCRIPTION) 
FROM
B_CEREMONY

結果:

'A7'1 , ,4F

私を助けてください。

うわー、この解決策が働いている:

SELECT listagg(convert(DESCRIPTION, 'UTF8', 'AL16UTF16'),' , ') within group 
(order by DESCRIPTION) 
FROM  B_CEREMONY;
0