web-dev-qa-db-ja.com

複数の値列を持つテーブルの回転

これがcrosstab()で実行できるかどうかはわかりません。 この質問/回答 を参照として使用していますが、クエリをまとめるのが困難です。
私のテーブルは次のようになっていますが、週の列に50以上の異なる値があるだけです。

+------+----+--------+---------+--------+
| week | id | param1 | param2  | param3 |
+------+----+--------+---------+--------+
|    1 |  1 |     13 |      10 |     12 |
|    1 |  2 |     12 |      11 |     44 |
|    2 |  1 |     34 |      33 |      3 |
|    2 |  2 |      3 |      44 |      3 |
+------+----+--------+---------+--------+

次のように回転させたい:

+----+----------+-----------+----------+----------+----------+----------+-----+
| id | w1param1 | w1param2  | w1param3 | w2param1 | w2param2 | w2param3 | ... |
+----+----------+-----------+----------+----------+----------+----------+-----+
|  1 |       13 |        10 |       12 |       34 |       33 |        3 | ... |
|  2 |       12 |        11 |       44 |        3 |       44 |        3 | ... |
+----+----------+-----------+----------+----------+----------+----------+-----+

ここで、週列はパラメーター列の番号付けに使用され、行は列になります。

3
Alok Shenoy

(2番目の形式の)crosstab()関数は、これらの列を入力として期待します。

  • 1 row_name
  • (0-n)extra
  • 1 category
  • 1value

見る:

特定の問題は、3value列を一度に処理しようとしていることです(param1param2param3)。入力テーブルはすでに「半分ピボット」されています。これを解決するにはさまざまな方法があります。 3つのクロス集計クエリを結合するのがおそらく最もクリーンです。 5週間のデモ:

SELECT *
FROM   crosstab(
     'SELECT id, week, param1
      FROM   tbl
      ORDER  BY 1,2'
   , 'SELECT generate_series(1,5)'
   ) ct1 (id int, w1p1 int, w1p2 int, w1p3 int, w1p4 int, w1p5 int)
JOIN   crosstab(
     'SELECT id, week, param2
      FROM   tbl
      ORDER  BY 1,2'
   , 'SELECT generate_series(1,5)'
   ) ct2 (id int, w2p1 int, w2p2 int, w2p3 int, w2p4 int, w2p5 int) USING (id)
JOIN   crosstab(
     'SELECT id, week, param3
      FROM   tbl
      ORDER  BY 1,2'
   , 'SELECT generate_series(1,5)'
   ) ct3 (id int, w3p1 int, w3p2 int, w3p3 int, w3p4 int, w3p5 int) USING (id)

dbfiddle ここ

[INNER] JOINは安全です。すべてのインスタンスが同じ週のidsを返すことが保証されているためです。それ以外の場合はFULL JOIN

50週間を超えると、150カラム以上になります。それは本当にあなたが望むことですか?

3

列数が多いか不明な(動的)クロス集計クエリには、これらの列をクエリで列挙する必要があるという問題があります。

これを回避する方法はいくつかあります。

  • 動的SQLですべての列をピボットするクエリを生成します。その場合、サーバーへの2つのラウンドトリップが必要です。1つはクエリを生成するため、もう1つはクエリを実行するためです。

  • psqlが\ crosstabviewコマンドで実行できるように、クライアント側のピボット

生成されたピボットクエリ

質問の場合、生成されたクエリは次のようになります。

_  SELECT id,
  sum(case when week=1 then param1 end) AS w1param1,
  sum(case when week=1 then param2 end) AS w1param2,
  sum(case when week=1 then param3 end) AS w1param3,
  sum(case when week=2 then param1 end) AS w2param1,
  ... repeated until w50param3 or somesuch
  FROM tablename GROUP BY id ORDER BY id;
_

crosstab()はこのメソッドでは使用されないことに注意してください。

このターゲットクエリは、次のクエリを含む動的ステートメントとして生成できます。これは、基本的には週数と(1,2,3)のクロス積であり、SQLのビットがいくつかあります。

_SELECT concat('SELECT id,',
 (SELECT string_agg(clause, ',') FROM (
  SELECT format('sum(case when week=%s then param%s end) AS w%sparam%s',
 week, p, week, p) AS clause
FROM (SELECT DISTINCT week FROM tablename) s,
      generate_series(1,3) as p
ORDER BY week,p
  ) clauses),
  ' FROM tablename GROUP BY id ORDER BY id'
) AS pivot_query;
_

プログラミング言語では、このクエリを実行し、結果(単一行、単一列の文字列)をフェッチして、その結果をSQLクエリとして実行すると、150以上の列を持つ予期されるピボットデータが生成されます。

Psqlでは、これは\ gsetと変数を介して行うことができます

_ =# SELECT concat(
  ... etc...
  ) AS pivot_query \gset

 =# :pivot_query \g
_

結果をパディングや装飾なしでファイルに保存するには、次の設定を使用します。

_=# \pset footer off   \o /path/to/file.txt   \pset format unaligned
_

フィールド区切り文字は、_\pset fieldsep_または_\F_でも設定できます。

結果がファイルに格納されたら、_\o_のみを使用して、出力をターミナルにリダイレクトします。


クライアント側ピボット(psql)

クライアント側のpsqlのみのソリューションは、_\crosstabview_(psql 9.6+)の方が簡単です。

クエリはまったく異なります。最初に列を「アンピボット」して(_UNION ALL_サブクエリを使用して)、列_param1..2..3_を取得して、2番目の列に目的の列名を取得し、3番目の列にこれらのパラメータの対応する値を取得します。列、および4番目の列のピボットデータの目的の列の順序。

これは次のようになります。

_SELECT id, 'w' || week || 'param' || p, val,
  row_number() over (order by week,p) as rn
FROM (                                                                         
SELECT id, week, 1 AS p, param1 AS val FROM tablename
UNION ALL                                          
select id, week, 2 AS p, param2 AS val FROM tablename
UNION ALL
select id, week, 3 AS p, param3 AS val FROM tablename
) s ORDER BY id \crosstabview  1 2 3 4
_

ファイルにリダイレクトするには、前のソリューションと同じです。おそらく、装飾、パディング、フッターを削除し、セパレーターを選択します。

3
Daniel Vérité