将一些行更改为列

时间:2014-09-04 16:20:23

标签: sql postgresql stored-procedures pivot crosstab

我已尝试过很多方法在Postgres中按列“转置”行,但我仍然不能。 给出Postgres视图中的一组数据,如下所示:

视图1

ID|A_|B_|C_
--+--+--+--  
01|25|AA|1C  
02|50|BB|1C  
02|12|AA|2C  
03|27|BB|2C  
03|87|AA|3C  

我希望能够对此结果进行查询:

ID| A_1| B_1| C_1| A_2| B_2| C_2  
--+----+----+----+----+----+----
01| 25 | AA | 1C | __ | __ | __
02| 50 | BB | 1C | 12 | AA | 2C
03| 27 | BB | 2C | 87 | AA | 3C

这甚至可以尝试在Postgres中进行吗?我正在尝试做这样的事情

select * from crosstab(
$$select id, a_, b_, c_, rn
  from (
     select v.id, v.a_, v.b_, v.c_, row_number() over (partition by v.id order by v.id desc nulls last) as rn
     from view1 v
     ) sub
  order by id
$$
, 'values (1), (2), (3)'
) as t (id varchar, a_1 bigint, b_1 varchar, c_1 varchar, a_2 varchar, b_2 varchar, c_2 varchar)

1 个答案:

答案 0 :(得分:0)

crosstab()

的基础知识

如果您不熟悉交叉表(),请先阅读:

特殊困难

这里有两个复杂因素:

  • 涉及的数据类型是不均匀的。 (使我想要检查您的数据库设计。)共同点是将相关列转换为text

  • 你正在将交叉制表混合在一起(有时候称为“unpivot”)。

建立在稍微简化的表格上:

CREATE TEMP TABLE view1(
   id int
 , a  int
 , b  text
 , c  text
);

我看到两条基本路线:

路线1

首先将a,b,c转换为此表格。同时使用Postgres特定的unnest()。这里的解释:

SELECT id
     , unnest('{a,b,c}'::text[]) || rn          AS key
     , unnest(ARRAY[a::text, b::text, c::text]) AS val
FROM  (
    SELECT *, row_number() OVER (PARTITION BY id) AS rn  -- ORDER BY ??
    FROM   view1
    ) v;

假设每个id值只能最多两行。如何确定“第一”和“第二”是未定义的。结果(使用您的测试数据):

id  key  val
1   a1   25
1   b1   AA
1   c1   1C
2   a1   50
2   b1   BB
2   c1   1C
2   a2   12
2   b2   AA
2   c2   2C
3   a1   27
3   b1   BB
3   c1   2C
3   a2   87
3   b2   AA
3   c2   3C

SQL Fiddle.
其余的在sqlfiddle.com上是不可能的,因为没有安装附加模块tablefunc

将此查询提供给标准crosstab()电话。另外,您可能希望将a1a2投回到bigint外的int(或仅SELECT?):

SELECT id, a1::bigint, b1, c1, a2::bigint, b2, c2  -- or just SELECT *
FROM crosstab(
   $$
   SELECT id
         ,unnest('{a,b,c}'::text[]) || rn
         ,unnest(ARRAY[a::text, b::text, c::text])
   FROM  (
      SELECT *, row_number() OVER (PARTITION BY id) AS rn
      FROM   view1
      ) v
   $$
 , $$SELECT unnest('{a1,b1,c1,a2,b2,c2}'::text[])$$
) AS t (id varchar, a1 text, b1 text, c1 text, a2 text, b2 text, c2 text);

VOILÀ。
要获得更加动态的解决方案,请结合以下答案:

路线2

分别为abc运行交叉表,并加入id上的派生表。