在 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
我有以下 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