PostgreSQL将列转换为行?转置? [英] PostgreSQL convert columns to rows? Transpose?

查看:136
本文介绍了PostgreSQL将列转换为行?转置?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个PostgreSQL函数(或表),该函数提供以下输出:

I have a PostgreSQL function (or table) which gives me the following output:

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

现在,我希望输出如下:

Now I want the Output as below:

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

该怎么做?

推荐答案

我的答案基于以下表格:

Basing my answer on a table of the form:

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

每一行都会返回一个新列.使用这样的动态返回类型,几乎不可能通过单次调用数据库来使其完全动态.通过两个步骤演示解决方案:

Each row results in a new column to return. With a dynamic return type like this, it's hardly possible to make this completely dynamic with a single call to the database. Demonstrating solutions with two steps:

  1. 生成查询
  2. 执行生成的查询

通常,这受表可容纳的最大列数限制.因此,对于行数多于1600(或更少)的表,则不是一种选择.详细信息:

Generally, this is limited by the maximum number of columns a table can hold. So not an option for tables with more than 1600 rows (or fewer). Details:

  • 完全动态的,适用于任何表.在两个位置提供表格名称:
  • Completely dynamic, works for any table. Provide the table name in two places:
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;

可以用单个参数包装到函数中...
生成以下形式的查询:

Could be wrapped into a function with a single parameter ...
Generates a query of the form:

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()

的简单解决方案

Simple solution with 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;

  • 对于具有多于两列的表,速度较慢.
  • 生成以下形式的查询:

    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
    

    结果相同.

    如果可以的话,请使用它.击败其他人.

    Use this if you can. Beats the rest.

    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示例中的那样.
    生成以下形式的查询:

    Operating with attnum instead of actual column names. Simpler and faster. Join the result to pg_attribute once more or integrate column names like in the pg 9.3 example.
    Generates a query of the form:

    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);
    

    这使用了一系列高级功能.太多解释了.

    This uses a whole range of advanced features. Just too much to explain.

    一个 unnest() 现在可以将多个数组并行嵌套.

    One unnest() can now take multiple arrays to unnest in parallel.

    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)
    

    在9.3版上运行的

    SQL Fiddle .

    SQL Fiddle running on pg 9.3.

    这篇关于PostgreSQL将列转换为行?转置?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆