如何将动态 json 键展平为 BigQuery 中的列?
Posted
技术标签:
【中文标题】如何将动态 json 键展平为 BigQuery 中的列?【英文标题】:How to flatten dynamic json key into columns in BigQuery? 【发布时间】:2021-06-25 22:58:40 【问题描述】:这是原始的 JSON 结构:
"tt": [
"box":
"type": "A"
,
"rr": ...
,
"box":
"type": "B"
,
"rr": ...
,
"box":
"type": "C"
,
"rr": ...
]
然后我使用下面的SQL命令得到tt
结构
with t1 as (
SELECT
rate_card,
JSON_EXTRACT_ARRAY(rate_card, '$.tt') as rr,
FROM `original_table_temp`
)
上一个 JSON 结构中的 rr
JSON 结构如下所示
"rr":
"10": <-------------------------
"tils":
"mdd":
"df":
"tif":
"sc": 17.85, <------------------
"evr": [
"p": 16.35, <---------------------
"t": null,
"nr?": false
]
,
"11": ...,
"12": ...,
...
以上所有 SQL 命令都在 BigQuery 中执行。
我有兴趣将box.type
、rr
的keys
(即rr
中的10、11、12 等)放入一列。
keys
旁边的列是sc
和p
最终表格的输出如下:
+------------+---------+---------------+
| box.type| rr | sc | p |
+------------+-------------------------+
| A | 10 | 17.85 | 16.35 |
| A | 11 |some value| .... |
......
| C | 12 | ..... | ......|
+------------+----------------------------+
目前,我设法通过 SQL BigQuery 中的硬编码获得了 sc
值。见下文:
SELECT
JSON_EXTRACT_SCALAR(rr_2, '$.rr.10.tils.mdd.df.tif.sc') AS sc,
FROM t1,
UNNEST(rr) rr_2
上面的方法效率不高,因为keys
很多,比如10、11、12等等。
-
如何将动态
keys
从rr
提取到列中?
如何不对 SQL 进行硬编码以获取 sc
和 p
?
在测试答案时,请随意删除 JSON 示例中的 <----------
。
这是一篇很长的文章。如果您需要更多信息,请告诉我。
感谢您的耐心和时间。
【问题讨论】:
您不“硬编码”的想法是尝试在嵌套结构中动态查找字段sc
和p
?
which is taken from a JSON array column
- 我建议您使用该数组显示原始 json - 因此我们可以更轻松地帮助您 - 而且我希望如果我们从该数组开始,它将只是超级简单的解决方案
@Isac,是的,你是对的。我想动态获取sc
,p
,而不需要写'$.rr.10.tils.mdd.df.tif.sc'
@MikhailBerlyant,我已经编辑了原始问题以包含原始 JSON 结构。如果您需要更多信息,请告诉我。
【参考方案1】:
考虑下面
execute immediate (
select string_agg("select " || key || ''' key
, JSON_EXTRACT_SCALAR(rr_2, '$.rr.''' || key || '''.tils.mdd.df.tif.sc') AS sc
, JSON_EXTRACT_SCALAR(rr_2, '$.rr.''' || key || '''.tils.mdd.df.tif.evr[0].p') AS p
from `project.dataset.table`''', " union all ")
from `project.dataset.table`, unnest(regexp_extract_all(regexp_replace(JSON_EXTRACT(rr_2, '$.rr'), r':.*?+', ''), r'"(.*?)"')) key
);
如果应用到原始示例中的 rr JSON
结构 - 输出是
【讨论】:
我如何使用with t1 as ( SELECT rate_card, JSON_EXTRACT_ARRAY(rate_card, '$.tt') as rr, FROM `original_table_temp` )
的答案?当我使用 Execute Immediate
时,我从 Google BigQuery 收到以下错误消息:Syntax error: Expected "(" or "," or keyword SELECT but got keyword EXECUTE at [38:1]
【参考方案2】:
具有非封闭键集的 JSON 对象不能很好地映射到面向 SQL 表的域中。
解决此问题的一种方法是将类似 key1: , key2: , ...
的 JSON 数据转换为类似 [key1, ..., key2, ..., ...]
的内容。
使用BigQuery javascript UDF...
CREATE TEMPORARY FUNCTION extract_tt(json STRING)
RETURNS STRUCT<
tt ARRAY<STRUCT<
box STRUCT<
type STRING
>,
rr ARRAY<STRUCT<
key STRING,
tils STRUCT<
mdd STRUCT<
df STRUCT<
tif STRUCT<
sc FLOAT64,
evr ARRAY<STRUCT<
p FLOAT64,
t STRING,
nr BOOL
>>
>
>
>
>
>>
>>
>
DETERMINISTIC
LANGUAGE js
AS
r"""
try
return JSON.parse(json,
(key, value) =>
if (key === 'rr' && value !== null && typeof value === 'object')
return Object.keys(value).map(key => ( key, ...value[key] ));
else
return value;
);
catch
""";
WITH extracted AS (
SELECT extract_tt(json).*
FROM UNNEST([
'"tt":["box":"type":"A","rr":"10":"tils":"mdd":"df":"tif":"sc":17.85,"evr":["p":16.35,"t":null,"nr":false],"11":,"box":"type":"B","rr":,"box":"type":"C","rr":"12":"tils":"mdd":"df":"tif":"sc":99]'
]) AS json
)
SELECT box.type, key, tils.mdd.df.tif.sc, tils.mdd.df.tif.evr[OFFSET(0)].p
FROM extracted, UNNEST(tt), UNNEST(rr);
这是解包rr
的结果,它可以平滑地表示rr
的动态键的任何程度的变化...
Row | type | key | sc | p |
---|---|---|---|---|
1 | A | 10 | 17.85 | 16.35 |
2 | A | 11 | null | null |
3 | C | 12 | 99.0 | null |
请注意,UDF 的返回类型经过精心设计,可以从 JSON 域映射到 SQL 域。只要类型匹配,BigQuery 对象投射器就会为您处理所有其他事情!
【讨论】:
请注意,使用 Javascript UDF 确实会带来一些性能损失。如果您的数据很大,您可能会注意到处理数据需要更长的时间。但是不会影响成本,因为您需要为处理的字节数而不是时间付费。以上是关于如何将动态 json 键展平为 BigQuery 中的列?的主要内容,如果未能解决你的问题,请参考以下文章
如何在展平嵌套字段后将数据从一个 bigquery 表流式插入到另一个表?
解析列中具有动态键的 JSON 值并将 JSON 转换为 BigQuery 中的记录列结构