如何将动态 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.typerrkeys(即rr 中的10、11、12 等)放入一列。

keys 旁边的列是scp

最终表格的输出如下:

+------------+---------+---------------+
| 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等等。

    如何将动态keysrr提取到列中? 如何不对 SQL 进行硬编码以获取 scp

在测试答案时,请随意删除 JSON 示例中的 &lt;----------

这是一篇很长的文章。如果您需要更多信息,请告诉我。

感谢您的耐心和时间。

【问题讨论】:

您不“硬编码”的想法是尝试在嵌套结构中动态查找字段scp which is taken from a JSON array column - 我建议您使用该数组显示原始 json - 因此我们可以更轻松地帮助您 - 而且我希望如果我们从该数组开始,它将只是超级简单的解决方案 @Isac,是的,你是对的。我想动态获取scp,而不需要写'$.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 中的记录列结构

Json - 在 pyspark 中展平键和值

Bigquery:是不是有一种 json 路径方法可以仅从具有动态键的 json 数组中提取值?

将 BigQuery 嵌套字段内容展平为新列而不是行

如何使用java将复杂的动态嵌套json插入bigquery