複数行の文字列データを指定したカラムの値ごとに1行に集約できるLISTAGG関数を試してみた #SnowflakeDB
※本エントリは、Snowflakeをもっと使いこなそう! Advent Calendar 2022の20日目の記事となります。
さがらです。
Snowflakeで、複数行の文字列データを指定したカラムの値ごとに1行に集約できるLISTAGG関数を試してみたので、その内容をまとめてみます。
試してみた
前提
あるフリマサイトで、出品者の一覧を示すmember
テーブルと、出品された商品一覧を示すitem
テーブルがあったとします。(ダミーデータなので値は適当です。)
member
テーブル
item
テーブル
このmember
テーブルとitem
テーブルは、member.id
カラムとitem.user_id
でJOIN可能です。
単純にJOINすると下図のようになります。
「各出品された商品の出品者情報を紐づける」という意味ではこのテーブルで良いのですが、「出品者1人ごとに1レコードでどんな商品を出品しているのかまとめたい」というケースもあると思います。これを、LISTAGG
関数で実現してみます!
LISTAGG関数を試してみる
item
テーブルに対して、LISTAGG
関数を様々な形で使ってみます。
まずは一番シンプルな使い方です。下記のように引数にカラムを1つだけ入れると、そのカラムの各行の値が1行に集約されて返ってきます。
select listagg(item_id) from item;
しかしこのままだと全ての文字列が繋がっていて見づらいですよね、そこで、LISTAGG
関数の第2引数に、各値の区切り文字を入れることができます!下記のクエリでは、第2引数に','
を入れて、カンマ区切りにしています。
select listagg(item_id, ',') from item;
また、LISTAGG
関数のあとにwithin group (order by [カラム名])
で、指定したカラムで出力する順序を指定することも可能です。
select listagg(item_id, ',') within group (order by item_id), listagg(item_name, ',') within group (order by item_id) from item;
さらに、distinct
オプションをつけることで、出力する値の重複削除も可能です。
select listagg(distinct user_id, ',') within group (order by user_id) from item;
また、これまでは1行にまとめることしかしていませんでしたが、GROUP BY
句と併せることで、指定したカラムの値ごとに行を集約することができます!この使い方が一番使う場面が多いと思います。
select user_id, listagg(item_id, ', ') within group (order by item_id) from item group by user_id;
ARRAY_CONSTRUCT
関数と併せて使うことで出力を配列にすることもできます!配列にしておけば、GET関数などで値を個別に取得できるので、なお便利ですね。
select user_id, array_construct(listagg(item_id, ', ') within group (order by item_id)) from item group by user_id;
itemテーブルをuser_id列のユニーク値ごとに1行にまとめてmemberテーブルとJOINする
ということで、最後のまとめとして、このitem
テーブルをuser_id
列のユニーク値ごとに1行にまとめてmember
テーブルとJOINすることを考えてみます。
まず、item
テーブルをuser_id
列のユニーク値ごとに1行にまとめるには下記のクエリを実行すればOKです。
select user_id, listagg(item_id, ', ') within group (order by item_id) as item_id, listagg(item_name, ', ') within group (order by item_id) as item_name, listagg(item_price, ', ') within group (order by item_id) as item_price from item group by user_id;
この上で、member
テーブルとJOINするには、下記のクエリを実行すればOKです。これで、やりたいことが出来ました!
with item_by_user as ( select user_id, listagg(item_id, ', ') within group (order by item_id) as item_id, listagg(item_name, ', ') within group (order by item_id) as item_name, listagg(item_price, ', ') within group (order by item_id) as item_price from item group by user_id ) select * from member left outer join item_by_user on member.id = item_by_user.user_id;
最後に
複数行の文字列データを指定したカラムの値ごとに1行に集約できるLISTAGG関数を試してみました。
複数行の値を1行にまとめたいケース、たまにあると思いますのでそんな時にご活用ください!