将 bigquery json 字符串转换为列

Posted

技术标签:

【中文标题】将 bigquery json 字符串转换为列【英文标题】:convert bigquery json string to columns 【发布时间】:2019-08-30 13:56:39 【问题描述】:

对于以字符串形式出现的 json 数据,我希望使用 JSON_EXTRACT_SCALAR 之类的内容,但结果列的数量可以灵活调整。

这里是示例数据——不同的行可以有不同的列名,json可以嵌套:

WITH `my_table` AS (
  SELECT '"sku_types":"\"id\":\"5433306\",\"product_code\":\"adfklj_ewkj\"","additional_info":"Face 30 ml","stock_level":"20+"' as json_string 
  union all 
  SELECT '"additional_info":"Face 100 ml","offer_info":"30%"' as json_string 
)
SELECT * 
from my_table;

我希望将这些数据提取到单独的列中:sku_types.id, sku_types.product_code, additional_info, stock_level, offer_info

这可以在 SQL 中完成还是需要 javascript

我事先不知道 json 字段的名称,所以我无法使用 JSON_EXTRACT_SCALARJSON_EXTRACT 来做到这一点。

【问题讨论】:

【参考方案1】:

以下 BigQuery 标准 SQL 示例

#standardSQL
CREATE TEMPORARY FUNCTION parseJson(y STRING)
RETURNS ARRAY<STRING>
LANGUAGE js AS """
  var z = new Array();
  processKey(JSON.parse(y), '');
  function processKey(node, parent) 
    Object.keys(node).map(function(key) 
      value = node[key].toString();
      if (value !== '[object Object]') 
        if (parent !== '' && parent.substr(parent.length-1) !== '.') 
          z.push(parent + '.' + key + ':' + value)
         else 
          z.push(key + ':' + value)
        
       else 
        if (parent !== '' && parent.substr(parent.length-1) !== '.') parent += '.';
        processKey(node[key], parent + key);
      ;
    );         
  ;
  return z
""";
WITH `my_table` AS (
  SELECT 1 id, '"sku_types":"id":"5433306","product_code":"adfklj_ewkj","additional_info":"Face 30 ml","stock_level":"20+"' AS json_string UNION ALL 
  SELECT 2, '"additional_info":"Face 100 ml","offer_info":"30%"' AS json_string 
)
SELECT id, 
  ARRAY(
    SELECT AS STRUCT SPLIT(kv, ':')[OFFSET(0)] key, SPLIT(kv, ':')[SAFE_OFFSET(1)] value
    FROM UNNEST(parseJson(json_string)) kv
  ) params
FROM my_table

结果

Row id  params.key              params.value     
1   1   sku_types.id            5433306  
        sku_types.product_code  adfklj_ewkj  
        additional_info         Face 30 ml   
        stock_level             20+  
2   2   additional_info         Face 100 ml  
        offer_info              30%     

正如您所看到的,而不是将所有可能的属性解析为单独的列(这在此处完全不可能 - 除非您事先知道它们) - 上述方法将它们展平为参数数组中的键:值对

注意:在上面的示例中,我使用: 来构造键:值对,然后将它们拆分。如果您希望值具有此字符 - 您可以调整代码而不是 : 使用更独特的东西 - 例如 :::::::

快速更新地址评论: ...问题是一些json值是空的,在这种情况下它会抛出错误

#standardSQL
CREATE TEMPORARY FUNCTION parseJson(y STRING)
RETURNS ARRAY<STRING>
LANGUAGE js AS """
  var z = new Array();
  processKey(JSON.parse(y), '');
  function processKey(node, parent) 
    Object.keys(node).map(function(key) 
      if (!node[key]) 
        value = 'n/a'
       else 
        value = node[key].toString();
      
      if (value !== '[object Object]') 
        if (parent !== '' && parent.substr(parent.length-1) !== '.') 
          z.push(parent + '.' + key + ':' + value)
         else 
          z.push(key + ':' + value)
        
       else 
        if (parent !== '' && parent.substr(parent.length-1) !== '.') parent += '.';
        processKey(node[key], parent + key);
      ;
    );         
  ;
  return z
""";
WITH `my_table` AS (
  SELECT 1 id, '"sku_types":"id":"5433306","product_code":"adfklj_ewkj","additional_info":"Face 30 ml","stock_level":"20+"' AS json_string UNION ALL 
  SELECT 2, '"additional_info":"Face 100 ml","offer_info":"30%"' AS json_string union all
  SELECT 3 as id , '"offer_info":"30%", "price":null' AS json_string  
)
SELECT id, 
  ARRAY(
    SELECT AS STRUCT SPLIT(kv, ':')[OFFSET(0)] key, SPLIT(kv, ':')[SAFE_OFFSET(1)] value
    FROM UNNEST(parseJson(json_string)) kv
  ) params
FROM my_table  

结果

Row id  params.key              params.value     
1   1   sku_types.id            5433306  
        sku_types.product_code  adfklj_ewkj  
        additional_info         Face 30 ml   
        stock_level             20+  
2   2   additional_info         Face 100 ml  
        offer_info              30%  
3   3   offer_info              30%  
        price                   n/a    

如你所见 - 我用 'n/a' 替换空值,但你可以应用任何你想要的逻辑

【讨论】:

这很有效,谢谢!我唯一的问题是某些 json 值为 null,在这种情况下它会抛出错误:... SELECT 3 as id , '"offer_info":"30%", "price":null' AS json_string ) 。 .. 我将在一天后检查这个案例,并更新我的答案以解决空问题:o) 感谢您的帮助! :) 当然,正如预期的那样 :o) - 下一篇文章/问题见!

以上是关于将 bigquery json 字符串转换为列的主要内容,如果未能解决你的问题,请参考以下文章

BigQuery:将数组中的键值对转换为列

BigQuery 标准 SQL 如何将行转换为列

如何将 Bigquery 重复记录转换为列?

在 BigQuery 中,将对象的字符串化数组转换为非字符串化

大查询 - 将数组/json 对象转置为列

BigQuery 自动将字符串转换为 int