Как я могу массивировать агрегат для столбца, где различные значения меньше, чем заданное число в Google BigQuery? - PullRequest
0 голосов
/ 27 января 2020

У меня есть такая таблица наборов данных в Google Big Query:

| 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, в этом случае я хочу преобразовать его в одну из приведенных ниже таблиц:

|     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 (пороговых) различных значений. Я изучил множество документов здесь , но не смог выяснить требуемый запрос. Может ли кто-нибудь помочь или указать в правильном направлении?

Редактировать: Я имею в виду одно решение, где я делаю что-то вроде этого:

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;

Это даст мне второй результат, но требует сложная конструкция запроса при условии, что я не знаю числа и имен столбцов заранее. Кроме того, это может пересекать ограничение в 100 МБ на строку для таблицы BigQuery, поскольку у меня будет более миллиарда строк в таблице. Просьба также предложить, если есть лучший способ сделать это.

Ответы [ 3 ]

1 голос
/ 28 января 2020

Как насчет:

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

Этот запрос обработал 20,6 ГБ за 11,9 с (полмиллиарда строк). Он вернул только одну строку, потому что каждая вторая строка имела более 1000 уникальных значений (мой порог).

enter image description here

Это традиционно 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

Предупреждение: он будет выполняться только в том случае, если в столбцах нет специальных значений, таких как кавычки или запятые. Если они у вас есть, это будет не так просто (но все же возможно).

0 голосов
/ 07 февраля 2020

@ 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 не сможет поместить массивы в один столбец (с такой ошибкой, как this: Элементы массива типов {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` 
  ))

И это сработало хорошо!

0 голосов
/ 29 января 2020

Ниже для BigQuery Standard 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    
...