・グループ化されたレコードの特定項目を文字列結合したい
・結合された項目の表示順を設定したい
SQLのグループ化を活用しているとき、グループ化された複数レコードのとある項目(特に文字列項目)を列挙したいときがあります。
また、順番も意識しなければユーザの利便性が損なわれることもあるため、並び順を制御する必要もあります。
今回はその方法を解説します。
イメージとしては下図のようなデータでグループ化する場合です。
列A | 列B | 列C | 列D |
---|---|---|---|
1 | 1 | 2 | 2時 |
1 | 2 | 3 | 3時 |
1 | 3 | 1 | 1時 |
2 | 1 | 2 | 店舗B |
2 | 2 | 1 | 店舗A |
完成形は、列Aを基準にグルーピングし、列Cの昇順の並びで列Dの文字列が各区切り文字で区切られながら文字列結合されて表示されることになります。
完成形のイメージは、下図の通りになります。
列A | 列D_ |
---|---|
1 | 1時,2時,3時 |
2 | 店舗A,店舗B |
前提
- SQL Server 2017以降
※本件で使用する関数が「SQL Server 2017」から使用可能になるためです。
グループ化したレコードの文字列結合
まずはグループ化された文字列の結合方法についてです。
使用する関数は「STRING_AGG」になります。
STRING_AGGは集計関数のため、グループ化しない構文では使用できません。
記述方法は次の通りです。
STRING_AGG ( [結合する列], [区切り文字] )
結合する列では、文字列結合の対象となる列を指定します。今回の例では、列Dが該当します。
区切り文字では、文字列結合する文字列間に挿入される文字列を指定します。今回の例では、「,(カンマ)」が該当します。
「”(シングルクォーテーション)」で囲むことを忘れないようにしてください。
コード例
STRING_AGGを使用する構文はこのようになります。
SELECT
列A
,STRING_AGG(列D, ',') AS 列D_
FROM
グループ化前データ
GROUP BY
列A
こちらの構文は、列Aをグループ化し、列DをSTRING_AGGを「,(カンマ)」区切りで結合しています。
実行した出力イメージは下図のとおりです。
列A | 列D_ |
---|---|
1 | 2時,3時,1時 |
2 | 店舗B,店舗A |
結合順序の指定
次は、文字列がどのような順序で結合するか指定する方法になります。
先ほどの出力イメージでは、列Dが順序良く並んでおらず、使用者にとって大変不親切な状態になっています。
原因
「順番通りに結合されてくれればいいのに」と思われるかもしれません。
では、なぜ順番通りに並ばないかを説明します。
それは並びを指定していない場合、グループ化される前のテーブルの並び順に左右されるからです。
例に挙げたグループ化前データでは、列Aの昇順で並んだあと列Bの昇順で並んでいるため、並んでほしい順番にならなかったのです。
この場合は並び順を指定する必要があります。
指定方法
並び順を指定するには、「WITHIN句」を使用します。
先ほどのSTRING_AGG構文に、次のような構文を付け加えます。
STRING_AGG ( [結合する列], [区切り文字] ) WITHIN GROUP ( ORDER BY [並び順] )
並び順では、どの列を基準に並び替えるかを指定します。
今回の例では、列Cが該当します。
コード例
ではWITHIN句を使用して、先ほどのSTRING_AGGのコード例を書き加えると次のようになります。
SELECT
列A
,STRING_AGG(列D, ',') WITHIN GROUP(ORDER BY 列C) AS 列D_
FROM
グループ化前データ
GROUP BY
列A
これによって、列D_の並びが列Cを基準に並び替えられるようになります。
実行した出力イメージは下図のとおりです。
列A | 列D_ |
---|---|
1 | 1時,2時,3時 |
2 | 店舗A,店舗B |