[英]Transpose table in PostgreSQL
I have a table in PostgreSQL like: 我在PostgreSQL中有一张表格,例如:
org_name | month_1 | month_2 | ... | month_12
---------------------------------------------
org1 | 20 | 30 | ... | 15
org2 | 34 | 27 | ... | 49
I need to transpose it to: 我需要将其转置为:
month | org1 | org2
----------------------
1 | 20 | 34
2 | 30 | 27
.. | .. | ..
12 | 15 | 49
I found next solution on stackoverflow: 我在stackoverflow上找到了下一个解决方案:
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
But it doesn't work with syntax error about 'for'. 但这不适用于有关“ for”的语法错误。 Where I'm wrong? 我哪里错了?
I've not been able to get your approach to work yet; 我还没办法使您工作。 but this multistep union seems to. 但是这种多步骤的结合似乎可以做到。
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
Maybe you would find unnest more palpable: 也许您会发现嵌套更加明显:
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
Actually, you need something like unpivot plus pivot here. 实际上,您需要在此处像“无枢轴加枢轴”之类的东西。
1. decompose the rows to get single entries per month ("unpivot"): 1.分解行以每月获取单个条目(“取消透视”):
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;
If there are more organizations, you have to add a WHERE
clause to pick the relevant ones: 如果组织更多,则必须添加WHERE
子句以选择相关的组织:
WHERE org_name IN ('org1', 'org2')
2. , feed this to crosstab()
to get your desired result: 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);
Voilá. Voilá。
The crosstab()
function is provided by the additional module tablefunc
, so this has to be installed. crosstab()
函数由附加模块tablefunc
,因此必须安装它。
Detailed instructions and links: 详细说明和链接:
Here is a basic function to build the SQL statement for any list of organizations and any list of months: 这里是构建组织任何列表和个月的任何列表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;
You can extract the list of organizations or months dynamically, too ... 您也可以动态提取组织或月份列表...
Call: 呼叫:
SELECT f_unpivot_colums('{org1, org2}', '{1,2,12}')
Produces above statement - which you can execute in turn. 产生以上语句-您可以依次执行。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.