【问题标题】:Is it possible to count the number of times each key has occurred in a column of JSON?是否可以计算每个键在 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。至少提供一些简单的示例来显示您的输入数据和预期结果。那将是我认为的最低限度
  • (我有相对粗糙的工作代码,它依赖于每个对象的有限数量的键和有限数量的键,但是如果没有这些限制,我想不出一种方法)
  • 澄清 - 您的输入示例代表三行或者是一行
  • 输入和输出都是三行,虽然这个数字匹配是巧合

标签: google-bigquery


【解决方案1】:

BigQuery 标准 SQL 如下

Enabling Standard SQLUser-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 作为视图。模式应该能够毫无问题地查询视图。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-09-06
  • 2023-02-06
  • 2016-01-24
  • 1970-01-01
  • 2018-07-17
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多