根据固定的列集展平 SQL 中的数据

flatten data in SQL based on fixed set of column

我遇到了扁平化数据的特定情况,需要帮助。我需要输出为列值不固定的扁平化数据。因此,我想将输出限制为固定的列集。

给定 Table 'test_table'

ID 姓名 属性
1 C1 xxx
2 C2 xyz
2 C3 zz

场景是,列名称可以有任何编号。对应于 ID 的值。我需要以每个 ID 字段一行的方式展平数据。由于名称字段因每个 ID 而异,我想将其展平以修复 3 列,如 Co1、Co2、Co3。输出应该类似于

ID Co1 Co1_Property 二氧化碳 Co2_Property Co3 Co3_Property
1 C1 xxx
2 C2 xyz C3 zz

想不出使用 Pivot 或聚合的解决方案。任何帮助将不胜感激。

标准做法是使用条件聚合。也就是说,要使用 CASE 表达式来选择哪一行转到哪一列,然后 MAX() 将多行折叠成单独的行...

SELECT
  id,
  MAX(CASE WHEN name = 'C1' THEN name     END)  AS co1,
  MAX(CASE WHEN name = 'C1' THEN property END)  AS co1_property,
  MAX(CASE WHEN name = 'C2' THEN name     END)  AS co2,
  MAX(CASE WHEN name = 'C2' THEN property END)  AS co2_property,
  MAX(CASE WHEN name = 'C3' THEN name     END)  AS co3,
  MAX(CASE WHEN name = 'C3' THEN property END)  AS co3_property
FROM
  yourTable
GROUP BY
  id

背景资料:

  • CASE 表达式中没有 ELSE 隐式意味着 ELSE NULL
  • 因此,目的是让每一列从每个输入行接收 NULL,除了被旋转到该列的行
  • 聚合,例如 MAX() 本质上跳过 NULL
  • MAX( {NULL,NULL,'xxx',NULL,NULL} ) 因此等于 'xxx'



类似的方法将值“捆绑”到左侧 (因此 NULL 值总是只出现在右侧...)

该方法首先使用 row_number() 为每一行赋予一个值,该值与您要将该行放入的列相对应..

WITH
  sorted AS
(
  SELECT
    *,
    ROW_NUMBER() OVER (PARTITION BY id ORDER BY name)  AS seq_num
  FROM
    yourTable
)
SELECT
  id,
  MAX(CASE WHEN seq_num = 1 THEN name     END)  AS co1,
  MAX(CASE WHEN seq_num = 1 THEN property END)  AS co1_property,
  MAX(CASE WHEN seq_num = 2 THEN name     END)  AS co2,
  MAX(CASE WHEN seq_num = 2 THEN property END)  AS co2_property,
  MAX(CASE WHEN seq_num = 3 THEN name     END)  AS co3,
  MAX(CASE WHEN seq_num = 3 THEN property END)  AS co3_property
FROM
  yourTable
GROUP BY
  id

您可以使用数组:

select id,
       array_agg(name order by name)[safe_ordinal(1)] as name_1,
       array_agg(property order by name)[safe_ordinal(1)] as property_1,
       array_agg(name order by name)[safe_ordinal(2)] as name_2,
       array_agg(property order by name)[safe_ordinal(2)] as property_2,
       array_agg(name order by name)[safe_ordinal(3)] as name_3,
       array_agg(property order by name)[safe_ordinal(3)] as property_3
from t
group by id;

当前的所有答案都过于冗长,并且一次又一次地大量重复相同的代码片段,如果您需要考虑更多列,则需要复制粘贴并添加更多行,这将使它更加冗长!
我的偏好是避免这种类型的编码,而是使用更通用的东西,如下例

select * from (
  select *, row_number() over(partition by id) col
  from `project.dataset.table`)
pivot (max(name) as name, max(property) as property for col in (1, 2, 3))            

如果应用于您问题中的示例数据 - 输出为

如果您想更改输出列的数量 - 您只需修改查询的 for col in (1, 2, 3) 部分即可。
例如,如果您想要 5 列 - 您可以使用 for col in (1, 2, 3, 4, 5) - 就这么简单!!!