35

次の出力を提供するPostgreSQL関数(またはテーブル)があります。

Sl.no    username    Designation    salary   etc..
 1        A           XYZ            10000    ...
 2        B           RTS            50000    ...
 3        C           QWE            20000    ...
 4        D           HGD            34343    ...

今、私は以下のような出力が欲しいです:

Sl.no            1       2        3       4       ...
 Username        A       B        C       D       ...
 Designation     XYZ     RTS      QWE     HGD     ...
 Salary          10000   50000    20000   34343   ...

これを行う方法?

4

5 に答える 5

36

次の形式の表に基づいて回答します。

CREATE TABLE tbl (
  sl_no int
, username text
, designation text
, salary int
);

各行は、返される新しい列になります。このような動的な戻り値の型では、データベースへの 1 回の呼び出しでこれを完全に動的にすることはほとんど不可能です。2 つのステップによるソリューションのデモンストレーション:

  1. クエリを生成
  2. 生成されたクエリを実行

一般に、これはテーブルが保持できる列の最大数によって制限されます。そのため、1600 行を超える (またはそれ以下の) テーブルのオプションではありません。詳細:

Postgres 9.4+

動的ソリューションcrosstab()

できる最初のものを使用してください。残りを打ち負かします。

SELECT 'SELECT *
FROM   crosstab(
       $ct$SELECT u.attnum, t.rn, u.val
        FROM  (SELECT row_number() OVER () AS rn, * FROM '
                              || attrelid::regclass || ') t
             , unnest(ARRAY[' || string_agg(quote_ident(attname)
                              || '::text', ',') || '])
                 WITH ORDINALITY u(val, attnum)
        ORDER  BY 1, 2$ct$
   ) t (attnum bigint, '
     || (SELECT string_agg('r'|| rn ||' text', ', ')
         FROM  (SELECT row_number() OVER () AS rn FROM tbl) t)
     || ')' AS sql
FROM   pg_attribute
WHERE  attrelid = 'tbl'::regclass
AND    attnum > 0
AND    NOT attisdropped
GROUP  BY attrelid;

attnum実際の列名の代わりに操作します。より簡単に、より速く。pg_attribute結果をもう一度結合するか、pg 9.3 の例のように列名を統合します。
次の形式のクエリを生成します。

SELECT *
FROM   crosstab(
   $ct$
   SELECT u.attnum, t.rn, u.val
   FROM  (SELECT row_number() OVER () AS rn, * FROM tbl) t
       , unnest(ARRAY[sl_no::text,username::text,designation::text,salary::text]) WITH ORDINALITY u(val, attnum)
   ORDER  BY 1, 2$ct$
   ) t (attnum bigint, r1 text, r2 text, r3 text, r4 text);

これには、さまざまな高度な機能が使用されます。説明するには多すぎます。

によるシンプルなソリューションunnest()

複数のunnest()配列を並行してネスト解除できるようになりました。

SELECT 'SELECT * FROM unnest(
  ''{sl_no, username, designation, salary}''::text[]
, ' || string_agg(quote_literal(ARRAY[sl_no::text, username::text, designation::text, salary::text])
              || '::text[]', E'\n, ')
    || E') \n AS t(col,' || string_agg('row' || sl_no, ',') || ')' AS sql
FROM   tbl;

結果:

SELECT * FROM unnest(
 '{sl_no, username, designation, salary}'::text[]
,'{10,Joe,Music,1234}'::text[]
,'{11,Bob,Movie,2345}'::text[]
,'{12,Dave,Theatre,2356}'::text[])
 AS t(col,row1,row2,row3,row4);

pg 9.6で実行されているSQL Fiddle

Postgres 9.3 以前

動的ソリューションcrosstab()

  • 完全に動的で、どのテーブルでも機能します。次の2か所にテーブル名を指定します。
SELECT 'SELECT *
FROM   crosstab(
       ''SELECT unnest(''' || quote_literal(array_agg(attname))
                           || '''::text[]) AS col
             , row_number() OVER ()
             , unnest(ARRAY[' || string_agg(quote_ident(attname)
                              || '::text', ',') || ']) AS val
        FROM   ' || attrelid::regclass || '
        ORDER  BY generate_series(1,' || count(*) || '), 2''
   ) t (col text, '
     || (SELECT string_agg('r'|| rn ||' text', ',')
         FROM (SELECT row_number() OVER () AS rn FROM tbl) t)
     || ')' AS sql
FROM   pg_attribute
WHERE  attrelid = 'tbl'::regclass
AND    attnum > 0
AND    NOT attisdropped
GROUP  BY attrelid;

単一のパラメーターを持つ関数にラップできます...
次の形式のクエリを生成します。

SELECT *
FROM   crosstab(
       'SELECT unnest(''{sl_no,username,designation,salary}''::text[]) AS col
             , row_number() OVER ()
             , unnest(ARRAY[sl_no::text,username::text,designation::text,salary::text]) AS val
        FROM   tbl
        ORDER  BY generate_series(1,4), 2'
   ) t (col text, r1 text,r2 text,r3 text,r4 text);

望ましい結果が得られます。

col         r1    r2      r3     r4
-----------------------------------
sl_no       1      2      3      4
username    A      B      C      D
designation XYZ    RTS    QWE    HGD
salary      10000  50000  20000  34343

によるシンプルなソリューションunnest()

SELECT 'SELECT unnest(''{sl_no, username, designation, salary}''::text[] AS col)
     , ' || string_agg('unnest('
                    || quote_literal(ARRAY[sl_no::text, username::text, designation::text, salary::text])
                    || '::text[]) AS row' || sl_no, E'\n     , ') AS sql
FROM   tbl;
  • 列が 2 つ以上あるテーブルでは遅い。

次の形式のクエリを生成します。

SELECT unnest('{sl_no, username, designation, salary}'::text[]) AS col
     , unnest('{10,Joe,Music,1234}'::text[]) AS row1
     , unnest('{11,Bob,Movie,2345}'::text[]) AS row2
     , unnest('{12,Dave,Theatre,2356}'::text[]) AS row3
     , unnest('{4,D,HGD,34343}'::text[]) AS row4

同じ結果です。

于 2012-12-30T00:32:26.733 に答える
30
SELECT
   unnest(array['Sl.no', 'username', 'Designation','salary']) AS "Columns",
   unnest(array[Sl.no, username, value3Count,salary]) AS "Values"
FROM view_name
ORDER BY "Columns"

参照:convertingColumnsToRows

于 2012-12-29T18:34:53.770 に答える
11

(私のように)bashスクリプトからこの情報が必要な場合は、psqlにテーブルの列を行として出力するように指示する簡単なコマンドラインスイッチがあることに注意してください。

psql mydbname -x -A -F= -c "SELECT * FROM foo WHERE id=123"

この-xオプションは、psql で列を行として出力するための鍵となります。

于 2016-03-16T18:57:05.847 に答える
5

私はErwinが上で指摘したよりも単純なアプローチをとっています.Postgresで私のために働いています(SQL標準をサポートするすべての主要なリレーショナルデータベースで動作するはずです).

クロス集計の代わりに単純に UNION を使用できます。

SELECT text 'a' AS "text" UNION SELECT 'b';

 text
------
 a
 b
(2 rows)

もちろん、それはあなたがこれを適用しようとしている場合に依存します。必要なフィールドが事前にわかっていることを考慮して、さまざまなテーブルをクエリする場合でもこのアプローチを使用できます。すなわち:

SELECT 'My first metric' as name, count(*) as total from first_table UNION
SELECT 'My second metric' as name, count(*) as total from second_table 

 name             | Total
------------------|--------
 My first metric  |     10
 My second metric |     20
(2 rows)

それはより保守しやすいアプローチです、IMHO。詳細については、このページをご覧ください: https://www.postgresql.org/docs/current/typeconv-union-case.html

于 2019-02-05T17:21:04.913 に答える
0

プレーン SQL または PL/pgSQL でこれを行う適切な方法はありません。

DBからデータを取得するアプリケーションでこれを行う方がはるかに優れています。

于 2012-12-29T19:00:25.740 に答える