BigQuery Merge JSon Документы - PullRequest
       30

BigQuery Merge JSon Документы

0 голосов
/ 22 марта 2019

Я хотел бы иметь таблицу со столбцом JSON. Этот столбец JSON может включать произвольные документы. Я хотел бы объединить эти документы в соответствии с их временной отметкой, которая доступна в другом столбце. Есть ли способ объединить эти документы JSON по их метке времени?

Вот пример:

at t3 time {a:"1", b:"2"}
at t2 time {b:"4"}
at t1 time {a:"4", c:"5"}

Я хочу создать {a:"1", b:"2", c:"5"} в качестве вывода. Возможно ли это в BigQuery?

Ответы [ 3 ]

1 голос
/ 22 марта 2019

Вот возможное решение с использованием стандартных функций SQL BigQuery с вашими данными:

#standardSQL
WITH test AS (
  SELECT '{"a":"1", "b":"2"}' AS json, 3 AS t UNION ALL
  SELECT '{"b":"4"}' AS json, 2 AS t UNION ALL 
  SELECT '{"a":"4", "c":"5"}' AS json, 1 AS t 
)
SELECT data_row, TO_JSON_STRING(data_row) AS json_row
FROM (
  SELECT 
    ARRAY_TO_STRING(ARRAY_AGG(a IGNORE NULLS ORDER BY t DESC LIMIT 1),'') AS a,
    ARRAY_TO_STRING(ARRAY_AGG(b IGNORE NULLS ORDER BY t DESC LIMIT 1),'') AS b,
    ARRAY_TO_STRING(ARRAY_AGG(c IGNORE NULLS ORDER BY t DESC LIMIT 1),'') AS c
  FROM(
    SELECT JSON_EXTRACT_SCALAR(json,'$.a') AS a,
    JSON_EXTRACT_SCALAR(json,'$.b') AS b,
    JSON_EXTRACT_SCALAR(json,'$.c') AS c, 
    t
    FROM test
  )
) AS data_row

Обратите внимание, что ARRAY_AGG используется только для поиска самого последнего значения без NULL для каждого документа, поэтому оно преобразуетсядо STRING с ARRAY_TO_STRING.Результат этого запроса, который должен быть желательным:

Row data_row.a  data_row.b  data_row.c  json_row     
1   1           2           5           {"a":"1","b":"2","c":"5"}   

Проблема с этим запросом заключается в том, что вам необходимо указать все документы (в данном случае a,b,c).

1 голос
/ 22 марта 2019

Ниже для BigQuery Standard SQL

#standardSQL
SELECT STRING_AGG(y, ', ' ORDER BY y) json
FROM (
  SELECT STRING_AGG(TRIM(x) ORDER BY t DESC LIMIT 1) y
  FROM `project.dataset.table`, 
    UNNEST(SPLIT(REGEXP_REPLACE(json, r'{|}', ''))) x
  GROUP BY TRIM(SPLIT(x, ':')[OFFSET(0)]) 
)   

Примечание: вышеприведенное решение универсально, и не не требует заранее знать имена атрибутов (например, a, b и т. Д.), А скорее анализирует и извлекает все, что найдет. Очевидно, он основан на предположении о простых jsons, как в ваших примерах

Вы можете проверить, поиграть с выше, используя пример данных из вашего вопроса, как в примере ниже

#standardSQL
WITH `project.dataset.table` AS (
  SELECT '{"a":"1", "b":"2"}' json, 3 t UNION ALL
  SELECT '{"b":"4"}', 2 UNION ALL 
  SELECT '{"a":"4", "c":"5"}', 1  
)
SELECT STRING_AGG(y, ', ' ORDER BY y) json
FROM (
  SELECT STRING_AGG(TRIM(x) ORDER BY t DESC LIMIT 1) y
  FROM `project.dataset.table`, 
    UNNEST(SPLIT(REGEXP_REPLACE(json, r'{|}', ''))) x
  GROUP BY TRIM(SPLIT(x, ':')[OFFSET(0)]) 
)

с результатом

Row json     
1   "a":"1", "b":"2", "c":"5"     

Поскольку (как я уже говорил) он достаточно универсален - вы можете добавлять строки с большим количеством атрибутов без изменения кода, как показано ниже

#standardSQL
WITH `project.dataset.table` AS (
  SELECT '{"a":"1", "b":"2"}' json, 3 t UNION ALL
  SELECT '{"b":"4"}', 2 UNION ALL 
  SELECT '{"a":"4", "c":"5"}', 1 UNION ALL
  SELECT '{"abc":"1", "xyz":"2"}', 3 UNION ALL
  SELECT '{"abc":"3", "vwy":"4"}', 3  
)
SELECT STRING_AGG(y, ', ' ORDER BY y) json
FROM (
  SELECT STRING_AGG(TRIM(x) ORDER BY t DESC LIMIT 1) y
  FROM `project.dataset.table`, 
    UNNEST(SPLIT(REGEXP_REPLACE(json, r'{|}', ''))) x
  GROUP BY TRIM(SPLIT(x, ':')[OFFSET(0)]) 
)

с результатом

Row json     
1   "a":"1", "abc":"1", "b":"2", "c":"5", "vwy":"4", "xyz":"2"   
1 голос
/ 22 марта 2019

Возможно, есть лучший способ, хотя первая мысль пришла мне в голову:

  1. Агрегировать несколько строк json, используя STRING_AGG с уникальным разделителем (как в следующем примере: '||||||')
  2. Используйте JavaScript UDF для анализа JSON / слияния / вывода в строку.
#standardSQL
CREATE TEMPORARY FUNCTION merge_json(json_string STRING)
RETURNS STRING
LANGUAGE js
AS 
"""
  // TODO 1: split json string with '||||||' to get multiple parts
  // .    2: parse each json parts into object
  //      3: merge objects in your own way

  // fake output, just to demonstrate the idea
  var obj = JSON.parse('{"a":"1", "b":"2", "c":"5"}')
  return JSON.stringify(obj);
""";
WITH
  sample_data AS (
  SELECT '{a:"1", b:"2"}' AS json, 1000 AS timestamp
  UNION ALL
  SELECT '{b:"4"}' AS json, 2000 AS timestamp
  UNION ALL 
  SELECT '{a:"4", c:"5"}' AS json, 1000 AS timestamp )

SELECT timestamp, merge_json(STRING_AGG(json, '||||||')) as joined_json
FROM sample_data
GROUP BY timestamp

Вывод:

output

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...