PostgreSQL覚書 crosstabでPIVOT

PostgreSQLでもOracleのPIVOTみたいなことをやりたいなぁ・・・と思って調べた。
crosstabらしい。
拡張機能らしい。
早速PgAdminで以下のSQLを実行して取り込む。

CREATE EXTENSION IF NOT EXISTS tablefunc;

いくつか関数が追加された中に、crosstabがあればOK。
で、続けてSQLを組み立てる。

今回は以下のように縦並びである店舗日別データ(qty)を全店SUM(qty)にして横並びにしてみる。
tab_shop_qty カラム構成(y |m |d |no |shop | qty)

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を横に並べる

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を横に並べる

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行しか出てこない始末となった。

う~ん、なんとかなんないのだろうか。他にうまくできる方法を探さなければ、「関数作成してカーソル利用」しか思いつく方法がない・・・

Add a Comment

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください