【问题标题】:PostgreSQL - from long to wide formatPostgreSQL - 从长格式到宽格式
【发布时间】:2019-07-18 14:50:05
【问题描述】:

假设我有一些长格式的表格,如下所示:

CREATE TEMP TABLE tmp (
  id int,  
  value varchar,
  id2 int,
  key int);

INSERT INTO tmp VALUES 
  (1, 87.1, 1444, 102),
  (2, 144.9, 13921, 3), 
  (3, 'A032333', 13921, 7),
  (4, 88.9, 13921, 102),
  (5, 'JDS-SJDDD', 13921, 101),
  (6, 90000, 13921, 140),
  (7, 101.1, 33113, 133),
  (8, 'KKL-KKIDD', 33113, 101),
  (9, 0, 33113, 239),
  (10, 933.1, 33113, 250); 

我需要将此数据转换为宽格式,如下所示:

CREATE TEMP TABLE tmp2 (
  id2 integer,
  k3 numeric,
  k7 varchar,
  k101 varchar,
  k102 numeric,
  k133 numeric,
  k140 int,
  k239 int,
  k250 numeric);

INSERT INTO tmp2 VALUES 
  (1444, NULL, NULL, NULL, 87.1, NULL, NULL, NULL, NULL),
  (13921, 144.9, 'A032333', 'JDS-SJDDD', 88.9, NULL, 90000, NULL, NULL), 
  (33113, NULL, NULL, 'KKL-KKIDD', NULL, 101.1, NULL, 0, 933.1);

我尝试了多个CASE WHEN:

SELECT id2, 
    CASE WHEN key = 3 THEN value END AS a3,
    CASE WHEN key = 7 THEN value END AS a7,
    CASE WHEN key = 101 THEN value END AS a101,
    CASE WHEN key = 102 THEN value END AS a102,
    CASE WHEN key = 133 THEN value END AS a133,
    CASE WHEN key = 140 THEN value END AS a140,
    CASE WHEN key = 239 THEN value END AS a239,
    CASE WHEN key = 250 THEN value END AS a250
FROM tmp;

但是,输出会为 id2 保留多行,而每个值仅保留一个就足够了。它可以如何调整?我在想GROUP BY + COALESCE 之类的东西,但COALESCE 会跨行查找值,我需要返回关于列的第一个非空值。更重要的是,这种方法似乎非常麻烦,因为我的原始数据将包含大约 2000 个结果列,因此使用 CASE WHEN 指定每一列会产生大量代码。有什么捷径吗?如果没有,如何实现?

【问题讨论】:

标签: sql postgresql


【解决方案1】:

Postgres 支持filter 关键字进行条件聚合,所以我推荐:

SELECT id2, 
       MAX(value) FILTER (WHERE key = 3) AS a3,
       MAX(value) FILTER (WHERE key = 7) AS a7,
       MAX(value) FILTER (WHERE key = 101) AS a101,
       MAX(value) FILTER (WHERE key = 102) AS a102,
       MAX(value) FILTER (WHERE key = 133) AS a133,
       MAX(value) FILTER (WHERE key = 140) AS a140,
       MAX(value) FILTER (WHERE key = 239) AS a239,
       MAX(value) FILTER (WHERE key = 250) AS a250
FROM tmp
GROUP BY id2;

但关键思想是GROUP BY

【讨论】:

  • 喜欢FILTER 方法与CASE stmt!谢谢你。来自tablefunccrosstab 非常好,但由于需要“引用”查询,因此并不适用于所有情况。
  • @apinstein 。 . .过滤器也快一点。如果您对filter 有任何疑问,可以提出问题。
【解决方案2】:

你需要group by id2并聚合:

SELECT id2, 
    max(CASE WHEN key = 3 THEN value END) AS a3,
    max(CASE WHEN key = 7 THEN value END) AS a7,
    max(CASE WHEN key = 101 THEN value END) AS a101,
    max(CASE WHEN key = 102 THEN value END) AS a102,
    max(CASE WHEN key = 133 THEN value END) AS a133,
    max(CASE WHEN key = 140 THEN value END) AS a140,
    max(CASE WHEN key = 239 THEN value END) AS a239,
    max(CASE WHEN key = 250 THEN value END) AS a250
FROM tmp
group by id2
order by id2

这将适用于您的示例数据。
请参阅demo
结果:

>   id2 | a3    | a7      | a101      | a102 | a133  | a140  | a239 | a250 
> ----: | :---- | :------ | :-------- | :--- | :---- | :---- | :--- | :----
>  1444 | null  | null    | null      | 87.1 | null  | null  | null | null 
> 13921 | 144.9 | A032333 | JDS-SJDDD | 88.9 | null  | 90000 | null | null 
> 33113 | null  | null    | KKL-KKIDD | null | 101.1 | null  | 0    | 933.1

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-12-24
    • 2015-08-05
    • 2020-11-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-27
    • 2021-05-12
    相关资源
    最近更新 更多