sql >> データベース >  >> RDS >> PostgreSQL

CASEおよびGROUPBYを使用したピボットの動的な代替手段

    追加モジュール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);
    


    1. Postgresqlは、特定の合計金額に達するまで選択します

    2. MariaDBでのSUBSTRING_INDEX()のしくみ

    3. MySQLの日付形式–知っておくべきこと

    4. LOAD_FILEを使用してファイルをMySQLBLOBにロードする方法は?