0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

GROUP BYで1対1対応の文字データをまとめる関数はどれでもいい【Snowflake SQL】

Posted at

初めに

数値データであれば合計を取ったり,平均を取ったりすればいいですが,
文字データの場合は合計も平均も取れないので,大抵は中身を全て配列に入れてしまうかすると思います.

例えば,以下のようなデータがあるとします.
(適当に作成したので旬がいつとかは無視でお願いします.)
「果物」の項目で集計したい場合,

日付 果物 英名 収穫担当 収穫量(kg)
9月1日 りんご apple 鈴木 4
9月5日 みかん orange 佐藤 2
9月10日 りんご apple 鈴木 3
9月12日 もも peach 田中 5
9月14日 りんご apple 高橋 3
9月18日 みかん orange 佐藤 2

以下のようにするのがよさそうです.(日付は表示しないようにする)

果物 英名 収穫担当 収穫量の合計(kg)
りんご apple [鈴木,高橋] 10
みかん orange [佐藤] 4
もも peach [田中] 5

「収穫担当」の列は,Snowflakeならarray_aggを,
「収穫量の合計」の列は,sumを使えば良いかと思います.

ただ,「英名」の列は,「果物」の列の要素と1対1で対応しています.
「収穫担当」の列同様,配列としてまとめてしまうことも可能ですが,他にもいろいろ方法がありそうなので,いろいろ調べてみました.

※本記事はSnowflake上での話になります.

候補

MAXを使う(クリックでコードを表示)
SELECT 果物, MAX(英名), array_agg(DISTINCT 収穫担当), sum(収穫量)
FROM <テーブル名>
GROUP BY 果物;

列に格納された数値で最大のものを得るための関数ですが,文字配列に対しても使用可能です.
GROUP BYでまとめた時に,「果物」の値に対応する文字列が一つしかないならば,これでOKです.

GROUP BYに入れてしまう.(クリックでコードを表示)
SELECT 果物, 英名, array_agg(DISTINCT 収穫担当), sum(収穫量)
FROM <テーブル名>
GROUP BY 果物, 英名;
  • 「果物」 = りんご というデータ
  • 「果物」 = りんご かつ 「英名」 = Apple というデータ

果物一つに対して英名が一つしかないのだから,上記でのどちらでまとめても得られる結果は同じになります.

array_aggを使う.(クリックでコードを表示)
SELECT 果物, array_agg(DISTINCT 英名), array_agg(DISTINCT 収穫担当), sum(収穫量)
FROM <テーブル名>
GROUP BY 果物, 英名;

[]がつきますが,「収穫担当」の佐藤,田中のような感じでまとめられます.

modeを使う.(クリックでコードを表示)
SELECT 果物, mode(英名), array_agg(DISTINCT 収穫担当), sum(収穫量)
FROM <テーブル名>
GROUP BY 果物;

最頻値を出力します.「果物」 = りんごのデータには,「英名」 = apple というデータしか入ってないので,当然りんごに対してはappleが得られますね.

他にも候補がありましたらコメントで教えていただけるとありがたいです.

メリット・デメリット

職場のデータで実行速度をまとめてみました.(試行回数3回)
(500万件→4万件に集約)

MAX GROUP BY array_agg mode
71 ms 50 ms 61 ms 56 ms
67 ms 69 ms 65 ms 64 ms
43 ms 68 ms 56 ms 55 ms

誤差!自分が試した限りではどれも変わらないっぽいです.

それぞれの関数に対してのメリット・デメリット(所感)ですが,

  • MAX : データ全件において,1対1対応していることを確認しないと使いづらい
  • GROUP BY : SELECTの部分にも,GROUP BYの部分にも列の名前を書かなければならないため面倒
  • array_agg : 1対1で対応していなくても使える
    (「みかん」という値に対して,orangeだけじゃなく,tangerineという値が格納されていても集計時にそれに気づくことができる.)
    (ただし,orangeに対してtangerineがごく少数でも,[orange, tangerine]とあたかも同数かのように表示されてしまう)
  • mode : 1対1対応していなくても使える.
    (orengeというスペルミスが少量あっても,表示されるのは大多数を占める正しい表記「orange」である.ただ,集計時にスペルミスがあることには気付けない.)

これぐらいでしょうか.

総括

array_aggが,イレギュラーにも対応しやすいですし.やはり便利でしょうか.

ただ,関数の使い方に自分ルールを設ければ,後からコードを見返した時に列の対情報がわかるのはメリットだと思います.

自分ルールの例:
・MAXを使っている列はGROUP BYで指定された列と1対1で対応している.
・array_aggを使っている列はGROUP BYで指定された列と1対1で対応していない.
・modeを使っている列はGROUP BYで指定された列とほとんどの場合1対1で対応しているが,例外もある.
・GROUP BYで複数の列が指定されている場合,その列同士は1対1で対応していない.

ここまで読んでいただきありがとうございました.

0
0
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?