【问题标题】:flatten data in SQL based on fixed set of column基于固定列集在 SQL 中展平数据
【发布时间】:2021-09-12 11:37:38
【问题描述】:

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

给定表'test_table'

ID Name Property
1 C1 xxx
2 C2 xyz
2 C3 zz

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

ID Co1 Co1_Property Co2 Co2_Property Co3 Co3_Property
1 C1 xxx null null
2 C2 xyz C3 zz

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

【问题讨论】:

  • I want to flatten it for fix 3 columns - 不清楚!如果您有超过 3 个(比如说 5 个)特定 ID 的条目怎么办?你想输出 5 列吗?
  • @MikhailBerlyant 根据要求,字段名称可以达到任何值,所以我想限制它以修复否。所有 ID 的列数。如果说任何 ID 有 5 个值,那么输出中将忽略 2/5。

标签: sql view google-bigquery flatten


【解决方案1】:

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

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) - 就这么简单!!!

【讨论】:

  • 你试过了吗?它对你有用吗?很高兴知道:o)
  • 谢谢米哈伊尔,这个解决方案很好,但在我的场景中,我有一个特殊的要求来为所有列添加前缀,例如我无法使用 Pivot 执行的 col1_name、col2_name。如果有可能,那么肯定会节省很多额外的代码。
【解决方案2】:

你可以使用数组:

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;

【讨论】:

    【解决方案3】:

    标准做法是使用条件聚合。也就是说,使用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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-05-23
      • 1970-01-01
      • 2019-06-19
      • 1970-01-01
      • 2018-10-02
      • 2022-11-12
      • 2016-01-08
      • 1970-01-01
      相关资源
      最近更新 更多