在 Postgres 中使用列名进行透视

Pivot with column name in Postgres

我有以下 table tbl:

column1 | column2  | column 3
-----------------------------------
1       | 'value1' |  3
2       | 'value2' |  4

如何使用列名进行“透视”以生成如下输出:

column1 |      1   |   2
column2 | 'value1' |'value2'
column3 |      3   |   4

如前所述,问题中未定义数据类型问题。

如果您同意所有结果列的类型为 text(每种数据类型都可以转换为 text),您可以使用以下之一:

普通SQL

WITH cte AS (
   SELECT nu.*
   FROM   tbl t
   , LATERAL (
      VALUES
        (1, t.column1::text)
      , (2, t.column2)
      , (3, t.column3::text)
      ) nu(rn, c)
   )
SELECT *
FROM   (TABLE cte OFFSET 0 LIMIT 3) c1
JOIN   (TABLE cte OFFSET 3 LIMIT 3) c2 USING (rn);

与有用的列名相同:

WITH cte AS (
   SELECT nu.*
   FROM   tbl t
   , LATERAL (
      VALUES
        ('column1', t.column1::text)
      , ('column2', t.column2)
      , ('column3', t.column3::text)
      ) nu(rn, c)
   )
SELECT * FROM (
   SELECT *
   FROM   (TABLE cte OFFSET 0 LIMIT 3) c1
   JOIN   (TABLE cte OFFSET 3 LIMIT 3) c2 USING (rn)
   ) t (key, row1, row2);

适用于任何现代版本的 Postgres。
SQL 字符串必须适应行数和列数。请参阅下面的 fiddles!

使用文档类型作为垫脚石

代码更短。
对于许多行和许多列,SQL 解决方案的性能可能会更好地扩展,因为中间派生的 table 更小。 (线程是有限的,因为在 Postgres 中你不能超过 ~ 1600 table 列。)

因为无论如何都转换为 text,所以 hstore 似乎是最有效的。参见:

  • Key value pair in PostgreSQL
SELECT key
     , arr[1] AS row1
     , arr[2] AS row2
FROM  (
   SELECT x.key, array_agg(x.value) AS arr
   FROM   tbl t, each(hstore(t)) x
   GROUP  BY 1
   ) sub
ORDER  BY 1;

从技术上讲,我们必须在 array_agg() 中强制执行正确的排序顺序,但这应该在没有显式 ORDER BY 的情况下起作用。为确保您可以添加一个:array_agg(x.value ORDER BY t.ctid) 由于缺少信息而使用 ctid

您可以对 (Postgres 9.3+) 中的 JSON 函数执行相同的操作。只需将 each(hstore(t) 替换为 json_each_text(row_to_json(t)。其余相同。

这些 fiddle 演示了如何扩展每个查询:

2 行 3 列的原始示例:
db<>fiddle here

扩展到 3 行 4 列:
db<>fiddle here