web-dev-qa-db-ja.com

PostgreSQLでピボットする方法

テーブルに次のデータを含める:

ID     Category   Value
1234   Cat01      V001
1234   Cat02      V002
1234   Cat03      V003
1234   Cat03      V004
1234   Cat03      V005

次の出力が必要です。

ID     Cat01  Cat02   Cat03
1234   V001   V002    V003
1234   V001   V002    V004
1234   V001   V002    V005

私が達成したい出力は、テーブルにすべての値を垂直方向に持ち、これらの値を水平方向に、カテゴリを列として持つ、一種のピボットテーブルです。しかし、複数の値を持ついくつかのカテゴリがあります。その場合、他のすべてのカテゴリの値を繰り返し、繰り返される値ごとに行を作成する必要があります

PostgreSQLではどのように実行できますか?

4
Aleps

これはトリッキーです。 crosstab() one (またはno) value per category を期待します各 row_name

この制限は次のように回避できます。

_SELECT id
     , COALESCE(cat01, max(cat01) OVER w)
     , COALESCE(cat02, max(cat02) OVER w)
     , COALESCE(cat03, max(cat03) OVER w)
FROM   crosstab(
   'SELECT id::text || row_number() OVER (PARTITION BY id, category ORDER BY value) * -1 AS ext_id
         , id, category, value
    FROM   tbl
    ORDER  BY ext_id, category, value'

   ,$$VALUES ('Cat01'::text), ('Cat02'), ('Cat03')$$
   ) AS ct (xid text, id int, cat01 text, cat02 text, cat03 text)
WINDOW w AS (PARTITION BY id);
_

希望する結果を返します。

どうやって?

  1. 既存のidからの拡張ID _ext_id_と、同じidのカテゴリの各値の行番号を追加します。このようにして、最も一般的なカテゴリの値と同じ数のidあたりの行を確保します。次のような派生テーブルを取得して、crosstab()を作成します。

    _ext_id   | id   | category | value
    ---------+------+----------+-------
    '1234-1' | 1234 | 'Cat01'  | 'V001'
    '1234-1' | 1234 | 'Cat02'  | 'V002'
    '1234-1' | 1234 | 'Cat03'  | 'V003'
    '1234-2' | 1234 | 'Cat03'  | 'V004'
    '1234-3' | 1234 | 'Cat03'  | 'V005'
    _
  2. これで、不足している属性の安全な2パラメータフォームを使用してcrosstab()にフィードできます。これに慣れていない場合は、まず基本を読んでください。

    元のidは「追加の列」として引き継がれます。見る:

  3. あなたの質問は解釈の余地を残します。私のソリューションでは、カテゴリごとに lowest の値を最初にペアにし、値がなくなるまで次の行を埋めていきます。 (他の方法でカテゴリごとに複数の値を組み合わせることができますが、定義されていません。)特定のidの値がカテゴリに不足している場合、残りはNULL値で埋められます。

  4. 最後のステップで、これらのNULL値をcategoryごとの各idの最大値に置き換えます。

    _COALESCE(cat01, max(cat01) OVER (PARTITION BY id, category))
    _

    これは事実上次と同じです:

    _max(cat01) OVER (PARTITION BY id, category)
    _

    値がNULLの場合にデフォルトでウィンドウ関数のみを使用する場合は、少し速くなることを期待しています。

3

CROSSTAB関数の使用例については、 ここ をご覧ください。また、同じスレッドでのErwin Brandstetterの投稿とリンク(特に、「基本的なcrosstab():」リンク)をよく見てください。

NULLsに注意してください(リンクの説明を参照)。

ソースからコンパイルされたPostgreSQLバージョンを使用していない場合、CROSSTAB関数にアクセスするために必要なのは入力することだけです。

CREATE EXTENSION tablefunc;

コマンドライン(EBリンクを参照)。

あなたの補足情報を完全に把握したかどうかはわかりませんが、おそらく私がクロスジョインステータスとスロット(カテゴリと値に相当するもの)をクロスさせるために使用したCTEアプローチが役立つかもしれません。そうでない場合は、コメントを拡張してください。 EBのコードも役立つかもしれません。

1
Vérace