postgresql 通过将列转换为行来转置表

oogrdqng  于 2023-11-18  发布在  PostgreSQL
关注(0)|答案(5)|浏览(196)

我有一个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   ...


如何做到这一点?

ygya80vv

ygya80vv1#

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"

字符串
引用:convertingColumnsToString

zu0ti5jz

zu0ti5jz2#

根据我的回答表的形式:

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

字符串
每一行都会产生一个要返回的新列。对于像这样的动态返回类型,很难通过对数据库的一次调用使其完全动态化。通过两个步骤演示解决方案:
1.生成查询
1.执行生成的查询
一般来说,这是由一个表可以容纳的最大列数限制的。因此,对于行数超过1600(或更少)的表,这不是一个选项。详细信息:

波斯特瑞斯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连接,或者像第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);

  • 小提琴 *

老古董

Posterre 9.3或更低版本

crosstab()动态解决方案

  • 完全动态,适用于任何表格。请在两个位置提供表格名称:
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;


可以用一个参数 Package 成一个函数...
生成以下形式的查询:

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;

  • 对于包含多个列的表,速度较慢。

生成以下形式的查询:

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


同样的结果。

6qqygrtg

6qqygrtg3#

如果(像我一样)你需要从bash脚本中获取这些信息,请注意,psql有一个简单的命令行开关,告诉它将表列输出为行:

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

字符串
-x选项是让psql将列输出为行的关键。

zazmityj

zazmityj4#

我有一个比Erwin上面提到的更简单的方法,它适用于Postgres(我认为它应该适用于所有支持SQL标准的主要关系数据库)
你可以使用UNION代替crosstab:

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)


这是一个更主要的方法,恕我直言。查看此页面以获取更多信息:https://www.postgresql.org/docs/current/typeconv-union-case.html

oymdgrw7

oymdgrw75#

在普通SQL或PL/pgSQL中没有正确的方法来做到这一点。
在从DB获取数据的应用程序中执行此操作会更好。

相关问题