是否可以计算每个键在 JSON 列中出现的次数?

Posted

技术标签:

【中文标题】是否可以计算每个键在 JSON 列中出现的次数?【英文标题】:Is it possible to count the number of times each key has occurred in a column of JSON? 【发布时间】:2016-10-12 00:31:42 【问题描述】:

我有一个 BigQuery 表,其中有一列包含 JSON。

我想输出每个键在列中出现的次数的计数,然后按计数降序排序。与所有键关联的值是1

每个对象有一个已知/有限数量的键,但我宁愿不依赖它,以防看到的最大对象发生变化。

总体上存在已知/有限数量的键,但我不想依赖在列表更改时枚举/更新列表。

例如输入:三行一列名为“json”

[
  "json": "'A': 1",
  "json": "'B': 1",
  "json": "'B': 1, 'C': 1"
]

例如输出:三行两列,分别命名为“key”和“count”

[
  "key": "B", "count": 2,
  "key": "A", "count": 1,
  "key": "C", "count": 1
]

考虑到我不想依赖每个对象和整体上有限数量的键,最简单的方法是什么?

【问题讨论】:

请编辑您的问题以显示您遇到问题的代码的Minimal, Complete, and Verifiable example,然后我们可以尝试帮助解决具体问题。你也可以阅读How to Ask。至少提供一些简单的示例来显示您的输入数据和预期结果。那将是我认为的最低限度 (我有相对粗糙的工作代码,它依赖于每个对象的有限数量的键和有限数量的键,但是如果没有这些限制,我想不出一种方法) 澄清 - 您的输入示例代表三行或者是一行 输入和输出都是三行,虽然这个数字匹配是巧合 【参考方案1】:

BigQuery 标准 SQL 如下

见Enabling Standard SQL和User-Defined Functions

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]') 
        z.push(key)
       else 
        if (parent !== '' && parent.substr(parent.length-1) !== '.') parent += '.';
        processKey(node[key], parent + key);
      ;
    );         
  ;
  return z
""";

WITH theTable AS (
  SELECT '"json":"A":"1"' AS json UNION ALL 
  SELECT '"json":"B":"1"' AS json UNION ALL
  SELECT '"json":"B":"1","C":"1"' AS json
)
SELECT key, COUNT(1) AS `count`
FROM theTable, UNNEST(parseJson(json)) AS key
GROUP BY key
ORDER BY 2 DESC

输出:

key count    
B       2    
A       1    
C       1    

注意:parseJson UDF 足够通用,可以处理任何 json,因此您可以例如尝试使用以下输入的上述代码,它仍然可以工作:

WITH theTable AS (
  SELECT '"json":"A":"1"' AS json UNION ALL 
  SELECT '"json":"B":"1"' AS json UNION ALL
  SELECT '"json":"B":"1","C":"1"' AS json UNION ALL
  SELECT '"A":"1"' AS json UNION ALL 
  SELECT '"B":"1"' AS json UNION ALL
  SELECT '"B":"1","C":"1"' AS json

)

输出:

key count    
B       4    
A       2    
C       2    

为 BigQuery Legacy SQL 添加了版本

为了简单起见,在这里展示和进一步测试 - 我在这里使用的是旧版 SQL UDF 的 inline version。旧版 SQL 中的 Inline version 不受官方支持 - 因此,如果它适用于您 - 您需要稍微改造一下 - 有关 BigQuery 旧版 SQL 中 UDF 的详细信息,请参阅 BigQuery User-Defined Functions

SELECT key, COUNT(1) as cnt
FROM JS((
  SELECT json FROM  
    (SELECT '"json":"A":"1"' AS json),
    (SELECT '"json":"B":"1"' AS json),
    (SELECT '"json":"B":"1","C":"1"' AS json),
    (SELECT '"A":"1"' AS json),
    (SELECT '"B":"1"' AS json),
    (SELECT '"B":"1","C":"1"' AS json)
  ),
  json,                                    // Input columns
  "[name: 'parent', type:'string',       // Output schema
   name: 'key', type:'string',
   name: 'value', type:'string']",
   "function(r, emit)                     // The function
      processKey(JSON.parse(r.json), '');
      function processKey(node, parent) 
        Object.keys(node).map(function(key) 
          value = node[key].toString();
          if (value !== '[object Object]') 
            emit(parent:parent, key:key, value:value);
           else 
            if (parent !== '' && parent.substr(parent.length-1) !== '.') parent += '.';
            processKey(node[key], parent + key);
          ;
        );         
      ;
    "
  )
GROUP BY key
ORDER BY cnt DESC  

【讨论】:

谢谢!不幸的是,这是针对尚不支持标准 SQL 的 Mode Analytics 仪表板。如果一周内没有旧版 SQL 解决方案,我会接受您的回答。 @AndreyFedorov - 刚刚为 bigquery legacy sql 添加了版本 您可以通过将#StandardSQL 放在查询的第一行(取决于仪表板在将文本发送到 BigQuery 之前是否修改文本)来将标准 SQL 与仪表板一起使用。【参考方案2】:

如果您禁用旧版 SQL,则可以使用新的 bigquery REGEX_EXTRACT_ALL 函数,这似乎正是您要查找的内容:https://cloud.google.com/bigquery/sql-reference/functions-and-operators#regexp_extract_all

【讨论】:

至于让查询进入模式,我建议在 bigquery 中将非传统 sql 作为视图。模式应该能够毫无问题地查询视图。

以上是关于是否可以计算每个键在 JSON 列中出现的次数?的主要内容,如果未能解决你的问题,请参考以下文章

计算 json 对象中键的出现次数 - IMPALA/HIVE

DataFrame中统计某几列中字符出现次数并比较

计算 PySpark SQL Join 中每个不同值在列中出现的次数

访问:计算 2 列中的出现次数 [关闭]

如何计算Dataframe中,列中元素连续出现次数

计算数据框列中列表中单词的出现次数