根据固定的列集展平 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)
- 就这么简单!!!
我遇到了扁平化数据的特定情况,需要帮助。我需要输出为列值不固定的扁平化数据。因此,我想将输出限制为固定的列集。
给定 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)
- 就这么简单!!!