将某些行更改为列

Posted

技术标签:

【中文标题】将某些行更改为列【英文标题】:Change some of the rows to columns 【发布时间】:2014-09-04 16:20:22 【问题描述】:

我已经尝试了很多方法在 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)

【问题讨论】:

您能展示一下到目前为止您尝试过的内容吗? 这在任何数据库中都是可能的,但如上所述,您需要展示一些工作原理 order by v.id 在您的窗口子句中没有意义。您如何确定每组相同ID 中的“第一”和“第二”值? 【参考方案1】:

crosstab() 的基础知识

如果您不熟悉 crosstab(),请先阅读以下内容:

PostgreSQL Crosstab Query

特殊难度

这里有两个并发症:

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

您将交叉制表与相反的(有时称为“unpivot”)混合在一起。

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

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

我看到了两条基本路线:

路线 1

首先将 a、b、c 还原为这种形式。并行使用 Postgres 特定的 unnest()。此处说明:

Is there something like a zip() function in PostgreSQL that combines two arrays?
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() 调用。另外,您可能想在外部SELECT 中将a1a2 转换回bigint(或只是int?):

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);

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

How to unpivot a table in PostgreSQL Dynamic alternative to pivot with CASE and GROUP BY

路线 2

分别为abc 运行交叉表,并在id 上连接派生表。

【讨论】:

非常感谢您的帮助!我会用它

以上是关于将某些行更改为列的主要内容,如果未能解决你的问题,请参考以下文章

使用 Pivot 将行更改为列

在屏幕调整大小时将布局从行更改为网格

将 WebStorm TSLint 错误行更改为警告行

在 Oracle 中更新列值的过程

更改数据框中某些列的名称[重复]

我在配置 apache 中将“Listen 80”行更改为“Listen 8080”,但它仍然不起作用