【问题标题】:Flatten BigQuery String which resembles an array展平类似于数组的 BigQuery 字符串
【发布时间】:2019-10-03 03:32:18
【问题描述】:

我正在使用 BigQuery 标准 SQL 方言。

我有一个列,我知道它是一个 JSON 字典数组。

数组长度随行变化。

我想将其展平,以便可以访问数组中每个字典的 JSON 元素。

例如,假设我有两条记录。第一个 id 为 1,在 JSON 列中,有这个

[
    {"key1":"val1a", "key2": "val1b"},
    {"key1":"val1c", "key2": "val1d"}
]

第二个有一个id 为2,并且在JSON 列中有

[{"key1":"val2a", "key2":"val2b"}]

我的目标是

id | key1  | key2  | offset
---------------------------
1  | val1a | val1b |   1
1  | val1c | val1d |   2
2  | val2a | val2b |   1

(虽然我可以没有偏移列)

看起来像这样的东西可以工作......

WITH table AS (
SELECT 1 as id,['{"key1":"val1a", "key2": "val1b"}','{"key1":"val1c", "key2": "val1d"}'] as array_column
UNION ALL
SELECT 2 as id,['{"key1":"val2a", "key2":"val2b"}'] as array_column)

SELECT id,
    json_extract_scalar(flattened_array, '$.key1') as key1,
    json_extract_scalar(flattened_array, '$.key2') as key2
FROM table t 
CROSS JOIN UNNEST(t.array_column) AS flattened_array

事实上,该查询返回我期望的表(减去偏移列,添加起来很简单)

问题在于 BigQuery 不明白这个东西是一个类似 JSON 的字符串数组。它认为整个事情就是一根大绳子,我不知道如何说服它。编辑我的示例以模拟这种类型混淆说明了问题:

WITH table AS (
SELECT 1 as id,'[{"key1":"val1a", "key2": "val1b"},{"key1":"val1c", "key2": "val1d"}]' as array_column
UNION ALL
SELECT 2 as id,'[{"key1":"val2a", "key2":"val2b"}]' as array_column)

SELECT id,
    json_extract_scalar(flattened_array, '$.key1') as key1,
    json_extract_scalar(flattened_array, '$.key2') as key2
FROM table t 
CROSS JOIN UNNEST(t.array_column) AS flattened_array

这里,验证器会抱怨,因为 UNNEST 中引用的值必须是数组。 UNNEST 在 [29:23] 包含 STRING 类型的表达式

现在我们正处于问题的核心。是否有一些明显的方法可以让 BigQuery 理解这个字符串是一个有效的 JSON 字典数组?也许我忽略了一些JSON_* 函数会使数组变平?或者以某种方式将CAST 这个东西放到一个数组中?

【问题讨论】:

  • 如果数组始终具有相同的结构,您应该将 json 数据作为结构数组导入 - 将其转换为 bigquery 可以理解的格式。

标签: json google-bigquery


【解决方案1】:

您可以使用 BigQuery JavaScript UDF 以任何您喜欢的方式解析 JSON:

CREATE TEMP FUNCTION flatten_array(array_column STRING)
RETURNS ARRAY<STRUCT<key1 STRING, key2 STRING>>
LANGUAGE js
AS """
  return JSON.parse(array_column)
""";

WITH table AS (
SELECT 1 as id,'[{"key1":"val1a", "key2": "val1b"},{"key1":"val1c", "key2": "val1d"}]' as array_column
UNION ALL
SELECT 2 as id,'[{"key1":"val2a", "key2":"val2b"}]' as array_column)

SELECT id,
    key1,
    key2
FROM table t 
CROSS JOIN UNNEST(flatten_array(array_column)) AS flattened_array

为了获得更好的原生 BQ JSON 数组支持,vote issue 63716683 up,并订阅更新。

【讨论】:

    【解决方案2】:

    以下是 BigQuery 标准 SQL,如果您的 json 像您的示例中一样简单,我建议使用它

    #standardSQL
    SELECT id, key1, key2
    FROM table,
    UNNEST(REGEXP_EXTRACT_ALL(array_column, r'"key1"\s*:\s*"(.*?)"')) key1 WITH OFFSET
    JOIN UNNEST(REGEXP_EXTRACT_ALL(array_column, r'"key2"\s*:\s*"(.*?)"')) key2 WITH OFFSET
    USING (OFFSET)
    

    您可以使用您问题中的示例数据进行测试,如以下示例所示

    #standardSQL
    WITH table AS (
      SELECT 1 AS id,'[{"key1":"val1a", "key2": "val1b"},{"key1":"val1c", "key2": "val1d"}]' AS array_column UNION ALL
      SELECT 2 AS id,'[{"key1":"val2a", "key2":"val2b"}]' AS array_column
    )
    SELECT id, key1, key2
    FROM table,
    UNNEST(REGEXP_EXTRACT_ALL(array_column, r'"key1"\s*:\s*"(.*?)"')) key1 WITH OFFSET
    JOIN UNNEST(REGEXP_EXTRACT_ALL(array_column, r'"key2"\s*:\s*"(.*?)"')) key2 WITH OFFSET
    USING (OFFSET)
    

    结果

    Row id  key1    key2     
    1   1   val1a   val1b    
    2   1   val1c   val1d    
    3   2   val2a   val2b   
    

    不是 100% 肯定,但我觉得上面的 UDF 比 UDF 便宜 - 这仍然是一个不错的选择 :o) 特别是对于具有更复杂 json 的更通用的情况

    【讨论】:

    • 感谢您的回答!由于它的普遍性,我将“接受” UDF 答案,但我非常感谢您提供一个有效的解决方案来解决我概述的特定情况!
    • 当然。如果还没有,请考虑投票:o)
    猜你喜欢
    • 2022-10-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-09-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多