【问题标题】:How to flatten dynamic json key into columns in BigQuery?如何将动态 json 键展平为 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等等。

  1. 如何将动态keys从rr提取到列中?
  2. 如何不对 SQL 进行硬编码以获取 sc 和 p?

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

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

感谢您的耐心和时间。

【问题讨论】:

  • 您不“硬编码”的想法是尝试在嵌套结构中动态查找字段sc 和p?
  • which is taken from a JSON array column - 我建议您使用该数组显示原始 json - 因此我们可以更轻松地帮助您 - 而且我希望如果我们从该数组开始,它将只是超级简单的解决方案
  • @Isac,是的,你是对的。我想动态获取sc,p,而不需要写'$.rr.10.tils.mdd.df.tif.sc'
  • @MikhailBerlyant,我已经编辑了原始问题以包含原始 JSON 结构。如果您需要更多信息,请告诉我。

标签: sql json google-bigquery unnest


【解决方案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 确实会带来一些性能损失。如果您的数据很大,您可能会注意到处理数据需要更长的时间。但是不会影响成本,因为您需要为处理的字节数而不是时间付费。
猜你喜欢
  • 2021-05-24
  • 1970-01-01
  • 2022-10-23
  • 2020-04-10
  • 2020-01-18
  • 1970-01-01
  • 1970-01-01
  • 2021-02-17
  • 1970-01-01
相关资源
最近更新 更多