追加モジュールtablefuncをインストールしていない場合 、このコマンドを1回実行します データベースごと:
CREATE EXTENSION tablefunc;
質問への回答
あなたのケースのための非常に基本的なクロス集計ソリューション:
SELECT * FROM crosstab(
'SELECT bar, 1 AS cat, feh
FROM tbl_org
ORDER BY bar, feh')
AS ct (bar text, val1 int, val2 int, val3 int); -- more columns?
特別な難しさ ここに、カテゴリがないということです。 (cat
)ベーステーブル内。基本的な1パラメータ形式の場合 カテゴリとして機能するダミー値を持つダミー列を提供するだけです。とにかく値は無視されます。
これはまれなケースの1つです ここで、2番目のパラメータ crosstab()
の場合 関数は不要です 、すべてのNULL
この問題の定義により、値は右側のぶら下がっている列にのみ表示されます。また、順序は値によって決定できます。 。
実際のカテゴリがある場合 結果の値の順序を決定する名前の列には、2パラメータ形式が必要です。 crosstab()
の 。ここでは、ウィンドウ関数row_number()
を使用してカテゴリ列を合成します。 、ベースcrosstab()
on:
SELECT * FROM crosstab(
$$
SELECT bar, val, feh
FROM (
SELECT *, 'val' || row_number() OVER (PARTITION BY bar ORDER BY feh) AS val
FROM tbl_org
) x
ORDER BY 1, 2
$$
, $$VALUES ('val1'), ('val2'), ('val3')$$ -- more columns?
) AS ct (bar text, val1 int, val2 int, val3 int); -- more columns?
残りはほとんどありふれたものです。これらの密接に関連する回答で、より多くの説明とリンクを見つけてください。
基本:
crosstab()
に慣れていない場合は、最初にこれを読んでください 機能!
- PostgreSQLクロス集計クエリ
詳細:
- Tablefuncを使用して複数の列をピボットする
- テーブルと変更ログをPostgreSQLのビューにマージします
適切なテスト設定
それが、最初にテストケースを提供する方法です:
CREATE TEMP TABLE tbl_org (id int, feh int, bar text);
INSERT INTO tbl_org (id, feh, bar) VALUES
(1, 10, 'A')
, (2, 20, 'A')
, (3, 3, 'B')
, (4, 4, 'B')
, (5, 5, 'C')
, (6, 6, 'D')
, (7, 7, 'D')
, (8, 8, 'D');
動的クロス集計?
あまり動的ではありません 、まだ、@Clodoaldoがコメントしたように。動的リターンタイプは、plpgsqlでは実現が困難です。しかし、 それを回避する方法-いくつかの制限があります 。
したがって、残りの部分をさらに複雑にしないために、より単純なでデモンストレーションします。 テストケース:
CREATE TEMP TABLE tbl (row_name text, attrib text, val int);
INSERT INTO tbl (row_name, attrib, val) VALUES
('A', 'val1', 10)
, ('A', 'val2', 20)
, ('B', 'val1', 3)
, ('B', 'val2', 4)
, ('C', 'val1', 5)
, ('D', 'val3', 8)
, ('D', 'val1', 6)
, ('D', 'val2', 7);
電話:
SELECT * FROM crosstab('SELECT row_name, attrib, val FROM tbl ORDER BY 1,2')
AS ct (row_name text, val1 int, val2 int, val3 int);
返品:
row_name | val1 | val2 | val3
----------+------+------+------
A | 10 | 20 |
B | 3 | 4 |
C | 5 | |
D | 6 | 7 | 8
tablefunc
の組み込み機能 モジュール
tablefuncモジュールは、一般的なcrosstab()
用のシンプルなインフラストラクチャを提供します 列定義リストを提供せずに呼び出します。 C
で記述された多数の関数 (通常は非常に高速です):
crosstabN()
crosstab1()
-crosstab4()
事前定義されています。 1つのマイナーなポイント:すべてのtext
が必要で返されます 。したがって、integer
をキャストする必要があります 値。しかし、それは呼び出しを単純化します:
SELECT * FROM crosstab4('SELECT row_name, attrib, val::text -- cast!
FROM tbl ORDER BY 1,2')
結果:
row_name | category_1 | category_2 | category_3 | category_4
----------+------------+------------+------------+------------
A | 10 | 20 | |
B | 3 | 4 | |
C | 5 | | |
D | 6 | 7 | 8 |
カスタムcrosstab()
機能
その他の列の場合 またはその他のデータ型 、独自の複合型を作成します および機能 (1回)。
タイプ:
CREATE TYPE tablefunc_crosstab_int_5 AS (
row_name text, val1 int, val2 int, val3 int, val4 int, val5 int);
機能:
CREATE OR REPLACE FUNCTION crosstab_int_5(text)
RETURNS SETOF tablefunc_crosstab_int_5
AS '$libdir/tablefunc', 'crosstab' LANGUAGE c STABLE STRICT;
電話:
SELECT * FROM crosstab_int_5('SELECT row_name, attrib, val -- no cast!
FROM tbl ORDER BY 1,2');
結果:
row_name | val1 | val2 | val3 | val4 | val5
----------+------+------+------+------+------
A | 10 | 20 | | |
B | 3 | 4 | | |
C | 5 | | | |
D | 6 | 7 | 8 | |
1つ すべての人のための多形的で動的な関数
これは、tablefunc
でカバーされているものを超えています モジュール。
リターン型を動的にするために、この関連する回答で詳しく説明されている手法を使用したポリモーフィック型を使用します:
- PL / pgSQL関数をリファクタリングして、さまざまなSELECTクエリの出力を返します
1パラメータ形式:
CREATE OR REPLACE FUNCTION crosstab_n(_qry text, _rowtype anyelement)
RETURNS SETOF anyelement AS
$func$
BEGIN
RETURN QUERY EXECUTE
(SELECT format('SELECT * FROM crosstab(%L) t(%s)'
, _qry
, string_agg(quote_ident(attname) || ' ' || atttypid::regtype
, ', ' ORDER BY attnum))
FROM pg_attribute
WHERE attrelid = pg_typeof(_rowtype)::text::regclass
AND attnum > 0
AND NOT attisdropped);
END
$func$ LANGUAGE plpgsql;
2パラメータ形式のこのバリアントで過負荷:
CREATE OR REPLACE FUNCTION crosstab_n(_qry text, _cat_qry text, _rowtype anyelement)
RETURNS SETOF anyelement AS
$func$
BEGIN
RETURN QUERY EXECUTE
(SELECT format('SELECT * FROM crosstab(%L, %L) t(%s)'
, _qry, _cat_qry
, string_agg(quote_ident(attname) || ' ' || atttypid::regtype
, ', ' ORDER BY attnum))
FROM pg_attribute
WHERE attrelid = pg_typeof(_rowtype)::text::regclass
AND attnum > 0
AND NOT attisdropped);
END
$func$ LANGUAGE plpgsql;
pg_typeof(_rowtype)::text::regclass
:すべてのユーザー定義の複合型に対して定義された行型があるため、属性(列)はシステムカタログpg_attribute
にリストされます。 。それを取得するための高速レーン:登録されたタイプをキャストします(regtype
)からtext
このtext
をキャストします regclass
へ 。
複合タイプを1回作成する:
使用するすべての返品タイプを1回定義する必要があります:
CREATE TYPE tablefunc_crosstab_int_3 AS (
row_name text, val1 int, val2 int, val3 int);
CREATE TYPE tablefunc_crosstab_int_4 AS (
row_name text, val1 int, val2 int, val3 int, val4 int);
...
アドホックコールの場合は、一時テーブルを作成することもできます 同じ(一時的な)効果に:
CREATE TEMP TABLE temp_xtype7 AS (
row_name text, x1 int, x2 int, x3 int, x4 int, x5 int, x6 int, x7 int);
または、既存のテーブル、ビュー、または可能であればマテリアライズドビューのタイプを使用します。
電話
上記の行タイプの使用:
1パラメータ形式(欠落値なし):
SELECT * FROM crosstab_n(
'SELECT row_name, attrib, val FROM tbl ORDER BY 1,2'
, NULL::tablefunc_crosstab_int_3);
2パラメータ形式(一部の値が欠落している可能性があります):
SELECT * FROM crosstab_n(
'SELECT row_name, attrib, val FROM tbl ORDER BY 1'
, $$VALUES ('val1'), ('val2'), ('val3')$$
, NULL::tablefunc_crosstab_int_3);
この1つの関数 crosstabN()
tablefunc
によって提供されるフレームワーク モジュールには、モジュールごとに個別の関数が必要です。
上記のようにタイプに順番に名前を付けた場合は、太字の数字を置き換えるだけで済みます。ベーステーブルでカテゴリの最大数を見つけるには:
SELECT max(count(*)) OVER () FROM tbl -- returns 3
GROUP BY row_name
LIMIT 1;
個々の列が必要な場合は、これとほぼ同じくらい動的です。 。 @Clocoaldoで示されるような配列、単純なテキスト表現、またはjson
のようなドキュメントタイプでラップされた結果 またはhstore
任意の数のカテゴリに対して動的に機能します。
免責事項:
ユーザー入力がコードに変換されると、常に潜在的に危険です。これをSQLインジェクションに使用できないことを確認してください。信頼できないユーザーからの入力を(直接)受け入れないでください。
元の質問を求める:
SELECT * FROM crosstab_n('SELECT bar, 1, feh FROM tbl_org ORDER BY 1,2'
, NULL::tablefunc_crosstab_int_3);