在 Google BigQuery 中,如何在不同值小于给定数字的情况下对每列进行数组聚合?

How can I array aggregate per column where distinct values are less than a given number in Google BigQuery?

我在 Google Big Query 中有一个像这样的数据集 table:

| col1 | col2 | col3 | col4 | col5 | col6 |
-------------------------------------------
|  a1  |  b1  |  c1  |  d1  |  e2  |  f1  |
|  a2  |  b2  |  c2  |  d1  |  e2  |  f2  |
|  a1  |  b3  |  c3  |  d1  |  e3  |  f2  |
|  a2  |  b1  |  c4  |  d1  |  e4  |  f2  |
|  a1  |  b2  |  c5  |  d1  |  e5  |  f2  |

假设给定的阈值为 4,在这种情况下,我想将其转换为下面给出的 table 之一:

|     col1     |    col2    |    col4    |    col5     |    col6    |
---------------------------------------------------------------------
|    [a1,a2]   |  [b1,b2,b] |    [d1]    |[e2,e3,e4,e5]|   [f1,f2]  |

或者像这样:

| col  | values        |
------------------------
| col1 | [a1,a2]       |
| col2 | [b1,b2,b]     |
| col4 | [d1]          |
| col5 | [e2,e3,e4,e5] |
| col6 | [f1,f2]       |

请注意,col3 已被删除,因为它包含超过 4 个(阈值)不同的值。我浏览了很多文档 here 但无法找出所需的查询。有人可以帮忙或指出正确的方向吗?

编辑:我有一个解决方案,我会这样做:

select * from (select 'col1', array_aggregate(distinct col1) as values union all
select 'col2', array_aggregate(distinct col2) as values union all
select 'col3', array_aggregate(distinct col3) as values union all
select 'col4', array_aggregate(distinct col4) as values union all
select 'col5', array_aggregate(distinct col5) as values) X where array_length(values) > 4;

这会给我第二个结果,但需要复杂的查询构造,前提是我事先不知道列的数量和名称。此外,这可能会超过 BigQuery table 每行 100MB 的限制,因为我将在 table 中拥有超过 10 亿行。如果有更好的方法,还请提出建议。

怎么样:

WITH arrays AS (
  SELECT * FROM UNNEST((
    SELECT [
      STRUCT("col_repo_name" AS col, ARRAY_AGG(DISTINCT repo.name IGNORE NULLS LIMIT 1001) AS values)
      , ('col_actor_login', ARRAY_AGG(DISTINCT actor.login IGNORE NULLS LIMIT 1001))
      , ('col_type', ARRAY_AGG(DISTINCT type IGNORE NULLS LIMIT 1001))
      , ('col_org_login', ARRAY_AGG(DISTINCT org.login IGNORE NULLS LIMIT 1001))
      ]
    FROM `githubarchive.year.2017` 
  ))
)

SELECT *
FROM arrays
WHERE ARRAY_LENGTH(values)<=1000

此查询在 11.9 秒内处理了 20.6GB(5 亿行)。它只返回一行,因为每隔一行都有超过 1000 个唯一值(我的阈值)。

这是传统的 SQL -- 但请参阅此处更简单的查询,它会产生类似的结果:

SELECT col, ARRAY_AGG(DISTINCT value IGNORE NULLS LIMIT 1001) values
FROM (
  SELECT REGEXP_EXTRACT(x, r'"([^\"]*)"') col , REGEXP_EXTRACT(x, r'":"([^\"]*)"') value
  FROM (
    SELECT SPLIT(TO_JSON_STRING(STRUCT(repo.name, actor.login, type, org.login)), ',') x
    FROM `githubarchive.year.2017`
  ), UNNEST(x) x
)
GROUP BY col
HAVING ARRAY_LENGTH(values)<=1000

# 17.0 sec elapsed, 20.6 GB processed

警告:只有在列中没有特殊值(如引号或逗号)时才会 运行。如果你有这些,它就不会那么简单(但仍然可能)。

以下适用于 BigQuery 标准 SQL

#standardSQL
SELECT col, STRING_AGG(DISTINCT value) `values`
FROM (
  SELECT 
    TRIM(z[OFFSET(0)], '"') col,
    TRIM(z[OFFSET(1)], '"') value
  FROM `project.dataset.table` t,
  UNNEST(SPLIT(TRIM(to_JSON_STRING(t), '{}'))) kv,
  UNNEST([STRUCT(SPLIT(kv, ':') AS z)])
)
GROUP BY col 
HAVING COUNT(DISTINCT value) < 5

您可以使用您问题中的示例数据来测试和玩弄上面的内容 - 结果将是

Row col     values   
1   col1    a1,a2    
2   col2    b1,b2,b3     
3   col4    d1   
4   col5    e2,e3,e4,e5  
5   col6    f1,f2    

@FelipeHoffa 我能够在我的用例查询中稍加修改就可以使用你的想法。

SELECT * FROM UNNEST((
    SELECT [
      STRUCT("col_repo_name" AS col, ARRAY_AGG(DISTINCT repo.name IGNORE NULLS LIMIT 1001) AS values)
      , ('col_actor_login', ARRAY_AGG(DISTINCT actor.login IGNORE NULLS LIMIT 1001))
      , ('col_type', ARRAY_AGG(DISTINCT type IGNORE NULLS LIMIT 1001))
      , ('col_org_login', ARRAY_AGG(DISTINCT org.login IGNORE NULLS LIMIT 1001))
      ]
    FROM `githubarchive.year.2017` 
  ))

结构数组上的 UNNEST 将无法正常工作,因为基础列将具有不同的数据类型,并且 BigQuery 将无法将数组放在单个列下(错误如下:类型为 {STRUCT>, STRUCT>} 的数组元素没有公共超类型)。我将其修改为类似这样的内容以满足我的用例。

SELECT * FROM UNNEST((
    SELECT [
      STRUCT("col_repo_name" AS col, to_json_string(ARRAY_AGG(DISTINCT repo.name IGNORE NULLS LIMIT 1001)) AS values)
      , ('col_actor_login', to_json_string(ARRAY_AGG(DISTINCT actor.login IGNORE NULLS LIMIT 1001)))
      , ('col_type', to_json_string(ARRAY_AGG(DISTINCT type IGNORE NULLS LIMIT 1001)))
      , ('col_org_login', to_json_string(ARRAY_AGG(DISTINCT org.login IGNORE NULLS LIMIT 1001)))
      ]
    FROM `githubarchive.year.2017` 
  ))

这很有效!