PostgreSQL中的转置表
Posted
技术标签:
【中文标题】PostgreSQL中的转置表【英文标题】:Transpose table in PostgreSQL 【发布时间】:2017-09-18 14:16:50 【问题描述】:我在 PostgreSQL 中有一个表,例如:
org_name | month_1 | month_2 | ... | month_12
---------------------------------------------
org1 | 20 | 30 | ... | 15
org2 | 34 | 27 | ... | 49
我需要将其转置为:
month | org1 | org2
----------------------
1 | 20 | 34
2 | 30 | 27
.. | .. | ..
12 | 15 | 49
我在 *** 上找到了下一个解决方案:
SELECT
*
FROM
(select org_name, monthes, value
from my_table
unpivot
(
value
for monthes in (month_2, month_3, month_4, month_5, month_6, month_7, month_8, month_9, month_10, month_11, month_12)
) unpiv
) src
pivot
(
sum(value)
for org_name in ('org1', 'org2')
) piv
但它不适用于有关“for”的语法错误。我哪里错了?
【问题讨论】:
同样的结果=( 那么你有答案了吗? 【参考方案1】:我还没能用你的方法来工作;但这个多步联合似乎是。
cte (mo, Org1, Org2) as (
SELECT 1, case when org_name = 'org1' then month_1 end as Org1, case when org_name = 'org2' then month_1 end as Org2 from myTable UNION ALL
SELECT 2, case when org_name = 'org1' then month_2 end as Org1, case when org_name = 'org2' then month_2 end as Org2 from myTable UNION ALL
SELECT 3, case when org_name = 'org1' then month_3 end as Org1, case when org_name = 'org2' then month_3 end as Org2 from myTable UNION ALL
...
SELECT 12, case when org_name = 'org1' then month_12 end as Org1, case when org_name = 'org2' then month_12 end as Org2 from myTable)
SELECT mo, max(org1) org1, max(org2) org2
FROM cte
GROUP BY mo
也许你会发现松散更明显:
With myTable (org_name, month_1, Month_2, month_3, Month_12) as (
Select 'org1',20,30,40,15 union all
Select 'org2',34,27,45,49),
cte (Mo,Org1,org2) as(select unnest(ARRAY[1,2,3,12]) AS Mo
, case when org_name='org1' then unnest(ARRAY[month_1, Month_2,month_3,Month_12]) end as Org1
, case when org_name='org2' then unnest(ARRAY[month_1, Month_2,month_3,Month_12]) end as Org2
from mytable)
Select mo,sum(org1) org1, sum(org2) org2
From cte
group by mo
【讨论】:
我有 12 列(月)和 10-12 行的组织,但是这个列表会被扩展,有没有比在代码中直接写每个列和行的名称更简单的方法? 【参考方案2】:实际上,您在这里需要类似 unpivot plus pivot 的东西。
1. 分解行以获得每个月的单个条目(“unpivot”):
SELECT mon, org_name, val
FROM tbl, LATERAL (
VALUES
(1, month_1)
, (2, month_2)
-- ... more
, (12, month_12)
) x(mon, val)
ORDER BY 1, 2;
如果有更多的组织,你必须添加一个WHERE
子句来选择相关的:
WHERE org_name IN ('org1', 'org2')
2.,将此反馈给crosstab()
以获得您想要的结果:
SELECT *
FROM crosstab($$
SELECT mon, org_name, val
FROM tbl, LATERAL (
VALUES
(1, month_1)
, (2, month_2)
-- ... more
, (12, month_12)
) x(mon, val)
WHERE org_name IN ('org1', 'org2')
ORDER BY 1, 2$$)
AS ct (month int, org1 int, org2 int);
瞧。
crosstab()
功能由附加模块 tablefunc
提供,因此必须安装。
详细说明和链接:
自动化:
这是为任何组织列表和任何月份列表构建 SQL 语句的基本函数:
CREATE OR REPLACE FUNCTION f_unpivot_colums(_orgs text[], _months int[])
RETURNS text AS
$func$
SELECT 'SELECT *
FROM crosstab($$
SELECT x.mon, t.org_name, x.val
FROM tbl t, LATERAL (
VALUES '
|| string_agg(format('(%s, t.%I)', m, 'month_' || m), ', ')
|| ') x(mon, val)
WHERE t.org_name = ANY (' || quote_literal(_orgs) || ')
ORDER BY 1, 2$$)
AS ct (month int, org1 int, org2 int)'
FROM unnest(_months) m
$func$ LANGUAGE sql;
您也可以动态提取组织或月份列表...
呼叫:
SELECT f_unpivot_colums('org1, org2', '1,2,12')
产生上述语句 - 您可以依次执行。
【讨论】:
以上是关于PostgreSQL中的转置表的主要内容,如果未能解决你的问题,请参考以下文章