[英]Google BigQuery: UNNEST where each different key becomes a column
我有这张表,其中有几列包含字典:payloadKV、metaKV 等。
我需要取消 dict 和 pivot 的结果,将每个键放在一列中,并将值放在该行、列的对应单元格中。 上面屏幕截图中所需的 output 将是:
+---------------------+-------+---------------+----------------+---------------------+-----+
| ingestTimestamp | ... | metadata.Area | metadata.Cell | metadata.Department | ... |
+---------------------+-------+---------------+----------------+---------------------+-----+
| 2022-03-23 02:34:41 | ... | MC | 0010 | 0752 | ... |
| ... | ... | ... | ... | ... | ... |
+---------------------+-------+---------------+----------------+---------------------+-----+
这些字典中的每一个都有任意数量的键/值,可以是数百个,我无法事先知道键名,所以我需要一些通用表达式来提取它们。
我已经看到了如何通过硬编码来提取所需密钥的示例,但我似乎找不到通用的方法来做到这一点。
可以使用 BigQuery PIVOT
以及EXECUTE IMMEDIATE
相对轻松地完成,如下例所示
create temp table temp as (
select t.* except(payloadKV, metaKV), replace(key, '.', '_') key, value
from your_table t, unnest(payloadKV)
union all
select t.* except(payloadKV, metaKV), replace(key, '.', '_') key, value
from your_table t, unnest(metaKV)
);
execute immediate (select '''
select * from temp pivot (any_value(value) for key in (''' ||
(select string_agg("'" || key || "'", ',' order by key) from (select distinct key from temp))
|| '''))
''')
如果应用于样本数据(类似于)您的问题
select '2022-03-23 02:34:41' ingestTimestamp, [
struct('payload.Area' as key, 'MC1' as value), ('payload.Cell', '00101'), ('payload.Department', '07521')] payloadKV, [
struct('metadata.Area' as key, 'MC' as value),('metadata.Cell', '0010'), ('metadata.Department', '0752')] metaKV
union all
select '2022-03-24 02:34:41' ingestTimestamp, [
struct('payload.Area' as key, 'MC2' as value), ('payload.Cell', '00102'), ('payload.Department', '07522')] payloadKV, [
struct('metadata.Area' as key, 'MC3' as value),('metadata.Cell', '00103'), ('metadata.Department', '07523')] metaKV
output 是
获得这样的剧本是一种挑战。 尽管您可能需要进行大量编码和试错,但这是可能的。 如果一个表真的很广泛,你可能想使用 python (正如martin weitzmann
所建议的那样)来检索列信息并创建你的脚本来获取你的数据。
您也可以只使用 BigQuery,但您可能会发现它很难在大表上实现,但这是我的方法,如果适合您的场景,您可以尝试这种方法:
create or replace table`projectid.dataset.table`
(
id INT64,
ingestTimeStamp date,
payloadKV STRUCT<id INT64,json STRING>,
metaKV STRUCT<id INT64,description STRING>
)
insert into `projectid.dataset.table`(id,ingestTimeStamp,payloadKV,metaKV)values(1,"2022-03-03",(100,'{"kardexid":11,"desc":"d1"}'),(100,"a desc1"));
insert into `projectid.dataset.table`(id,ingestTimeStamp,payloadKV,metaKV)values(2,"2022-03-04",(101,'{"kardexid":22,"desc":"d2"}'),(110,"a desc2"));
insert into `projectid.dataset.table`(id,ingestTimeStamp,payloadKV,metaKV)values(3,"2022-03-05",(102,'{"kardexid":34,"desc":"d3"}'),(120,"a desc3"));
insert into `projectid.dataset.table`(id,ingestTimeStamp,payloadKV,metaKV)values(4,"2022-03-06",(103,'{"kardexid":53,"desc":"d4"}'),(130,"a desc4"));
declare working_table string;
declare loop_col String;
declare query String;
declare single_col_names String;
declare nested_col_array ARRAY<STRING>;
declare nested_col_string String DEFAULT "";
# Set columns to work
set working_table = "table";
set single_col_names = (SELECT STRING_AGG(column_name) FROM `projectid.dataset.INFORMATION_SCHEMA.COLUMNS`
where table_name = working_table and data_type not like 'STRUCT%');
set nested_col_array = (SELECT ARRAY_AGG(column_name) FROM `projectid.dataset.INFORMATION_SCHEMA.COLUMNS`
where table_name = working_table and data_type like 'STRUCT%');
# Retrieve nested columns
FOR record IN
(SELECT * FROM unnest(nested_col_array) as col_names)
DO
SET loop_col = (SELECT CONCAT(column_name,
".",
REPLACE(ARRAY_TO_STRING(REGEXP_EXTRACT_ALL(data_type,r'[STRUCT<,INT64 STRING ]+(.+?) '),",")
,",",
CONCAT(",",column_name,".")))
FROM `projectid.dataset.INFORMATION_SCHEMA.COLUMNS`
where table_name = working_table and data_type like 'STRUCT%' and column_name=record.col_names);
SET nested_col_string = (SELECT CONCAT(nested_col_string,",",loop_col));
END FOR;
# build & run query
set query = (SELECT FORMAT("select %s%s from `projectid.dataset.table` order by 1",single_col_names,nested_col_string));
EXECUTE IMMEDIATE(query);
output:
ID | 摄取时间戳 | id_1 | json | id_2 | 描述 |
---|---|---|---|---|---|
1个 | 2022-03-03 | 100 | {“kardexid”:11,“desc”:“d1”} | 100 | 描述1 |
2个 | 2022-03-04 | 101 | {“kardexid”:22,“desc”:“d2”} | 110 | 描述2 |
3个 | 2022-03-05 | 102 | {“kardexid”:34,“desc”:“d3”} | 120 | 一个desc3 |
4个 | 2022-03-06 | 103 | {“kardexid”:53,“desc”:“d4”} | 130 | 一个desc4 |
如您所见,BigQuery 上的此类过程非常具有挑战性,因为您必须解析结构类型以获取内部列的名称、编写脚本并且绝对不是最佳选择。 为了这个问题,可以这样做,但这不是我推荐的。
在处理 BigQuery 时,您通常希望 go 用于较少资源投入的查询,并且只选择真正需要的东西。 您可以使用客户端库在 BigQuery 端执行更少的操作,并使用代码对从原始查询中获得的数据执行转换。
要创建此代码,我查阅了以下文档,检查它:
在数据库中,行是观察结果,列描述这些观察结果。 您想要不一致地描述观察结果 - 这是数据库的元级别,仅在这方面保持一致。
您可以使用未嵌套数组交叉连接, 然后使用 pivot ,但“您”仍然需要提前知道列名。
“你”可以是你本人,也可以是通过预先收集信息准备 SQL 语句的一段代码——例如,它可以是 python 中的一个自动化解决方案。 基本上
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.