PostgreSQLでもOracleのPIVOTみたいなことをやりたいなぁ・・・と思って調べた。
crosstabらしい。
拡張機能らしい。
早速PgAdminで以下のSQLを実行して取り込む。
1 | CREATE EXTENSION IF NOT EXISTS tablefunc; |
いくつか関数が追加された中に、crosstabがあればOK。
で、続けてSQLを組み立てる。
今回は以下のように縦並びである店舗日別データ(qty)を全店SUM(qty)にして横並びにしてみる。
tab_shop_qty カラム構成(y |m |d |no |shop | qty)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | CREATE TABLE tab_shop_qty ( y integer , m integer , d integer , no integer , shop character varying (10), qty numeric (5,0) ); INSERT INTO public .tab_shop_qty values (2019,1,1,1, 'abc' , 10); INSERT INTO public .tab_shop_qty values (2019,1,1,2, 'abc' , 8); INSERT INTO public .tab_shop_qty values (2019,1,2,1, 'abc' , 25); INSERT INTO public .tab_shop_qty values (2019,1,2,2, 'abc' , 15); INSERT INTO public .tab_shop_qty values (2019,1,1,1, 'efg' , 12); INSERT INTO public .tab_shop_qty values (2019,1,1,2, 'efg' , 7); INSERT INTO public .tab_shop_qty values (2019,1,1,2, 'efg' , -2); INSERT INTO public .tab_shop_qty values (2019,1,2,1, 'hij' , 20); |
店舗(shop)別に、日毎のqtyを横に並べる
1 2 3 4 5 6 7 | SELECT c.shop,c. "1" ,c. "2" ,...,c. "31" FROM crosstab( 'SELECT shop,d,SUM(qty) qty FROM tab_shop_qty WHERE y=2019 AND m=1 GROUP BY shop,d ORDER BY 1' , 'SELECT d FROM generate_series(1,31) d' ) AS c(shop varchar , "1" numeric , "2" numeric ,..., "31" numeric ) ORDER BY c.shop |
上の例だと、shopというカラム1つだけがキーになっているのでこのようなSQLで済むのだが、キーが複数カラムある場合は、第1引数のSQL文にORDER BY句を追加するという工夫が必要。
店舗,NO(shop,no)別に、日毎のqtyを横に並べる
1 2 3 4 5 6 7 | SELECT c.shop,c. no ,c. "1" ,c. "2" ,...,c. "31" FROM crosstab( 'SELECT shop,no,d,SUM(qty) qty FROM tab_shop_qty WHERE y=2019 AND m=1 GROUP BY shop,no,d ORDER BY 2' , 'SELECT d FROM generate_series(1,31) d' ) AS c(shop varchar , "1" numeric , "2" numeric ,..., "31" numeric ) ORDER BY c.shop,c. no |
ドキュメントurl: https://www.postgresql.jp/document/9.6/html/tablefunc.html では、
第1引数のORDER BY句は
「実際は、同じrow_name(キー項目のこと)を持つ値をまとめられるように、source_sql(第1引数のSQL文のこと)問い合わせでは常にORDER BY 1を指定すべきです。」
との記載があるが、「ORDER BY 1」なのであれば指定ありとなしでは変わりはなかった。
結局、ORDER BYの指定の仕方がイマイチわからなかったが、ORDER BYの1つめで指定したカラム数まではキー、次が第2引数のデータとの関連付け用、その次がPIVOTとして出力するデータ、かな・・・などと思った。
————————
<2019/08/08 追記>
いろいろ試していた時に発覚したことがあったので追記。
crosstabの第1引数のSQL文だが、グルーピングするカラムのうち、1つでもデータが1種類しかないカラムが存在した場合、うまく動作しないことが分かった。
上の説明文だと自分で後から見直しても、なんのこっちゃ!って思うと推測するので、追記しておくと・・・・
最後の例だとshop,noの2カラムがグループ,dが横並びにするカラム,qtyが横並びにするデータとなるのだが、
テーブルのレコードにshop=’abc’のデータ1種類しかない場合、crosstabがうまく動作しないということがある、ということだ。
ORDER BY 2までならうまくいくかもしれないが、少なくとも私が試してみたORDER BY 3(つまりグルーピングするカラムが3列)の場合、うまくマージされず、本来は何全行と出るはずの結果レコードが1行しか出てこない始末となった。
う~ん、なんとかなんないのだろうか。他にうまくできる方法を探さなければ、「関数作成してカーソル利用」しか思いつく方法がない・・・