Существует два основных способа хранения полуструктурированных данных, как в вашем примере:
Опция # 1: Сохранить строку JSON
Вы можете сохранитьdata
поле в виде строки JSON, а затем используйте функцию JSON_EXTRACT
, чтобы извлечь значения, которые он может найти, и он вернет NULL
для любого значения, которое он не может найти.
Так как вы упомянули о необходимости математического анализа полей, давайте сделаем простой SUM
для значений a
и b
:
# Creating an example table using the WITH statement, this would not be needed
# for a real table.
WITH records AS (
SELECT 1111 AS id, "{\"a\":27, \"b\":62, \"c\": \"string\"}" as data
UNION ALL
SELECT 2222 AS id, "{\"a\":27, \"c\": \"string\"}" as data
UNION ALL
SELECT 3333 AS id, "{\"a\":27}" as data
UNION ALL
SELECT 4444 AS id, "{\"a\":27, \"b\":62, \"c\": \"string\"}" as data
)
# Example Query
SELECT SUM(aValue) AS aSum, SUM(bValue) AS bSum FROM (
SELECT id,
CAST(JSON_EXTRACT(data, "$.a") AS INT64) AS aValue, # Extract & cast as an INT
CAST(JSON_EXTRACT(data, "$.b") AS INT64) AS bValue # Extract & cast as an INT
FROM records
)
# results
# Row | aSum | bSum
# 1 | 108 | 124
Есть некоторые плюсы и минусы в этомподход:
Плюсы
- Синтаксис довольно прост
- Меньше ошибок
Минусы
- Расходы на хранилище будут несколько выше, поскольку вам нужно хранить все символы для сериализации в JSON.
- Запросы будут выполняться медленнее, чем при использовании чистого собственного SQL.
Опция №2: повторяющиеся поля
BigQuery имеет поддержку для повторенияполя , позволяющие взять структуру и выразить ее в SQL.
Используя тот же пример, вот как мы это сделаем:
## Using a with to create a sample table
WITH records AS (SELECT * FROM UNNEST(ARRAY<STRUCT<id INT64, data ARRAY<STRUCT<key STRING, value STRING>>>>[
(1111, [("a","27"),("b","62"),("c","string")]),
(2222, [("a","27"),("c","string")]),
(3333, [("a","27")]),
(4444, [("a","27"),("b","62"),("c","string")])
])),
## Using another WITH table to take records and unnest them to be joined later
recordsUnnested AS (
SELECT id, key, value
FROM records, UNNEST(records.data) AS keyVals
)
SELECT SUM(aValue) AS aSum, SUM(bValue) AS bSum
FROM (
SELECT R.id, CAST(RA.value AS INT64) AS aValue, CAST(RB.value AS INT64) AS bValue
FROM records R
LEFT JOIN recordsUnnested RA ON R.id = RA.id AND RA.key = "a"
LEFT JOIN recordsUnnested RB ON R.id = RB.id AND RB.key = "b"
)
# results
# Row | aSum | bSum
# 1 | 108 | 124
Как видите, выполнить аналогичное действие все еще довольно сложно.Вам также по-прежнему необходимо хранить такие элементы, как строки, и CAST
их к другим значениям, когда это необходимо, поскольку вы не можете смешивать типы в повторяющемся поле.
Плюсы
- Размер магазина будет меньше, чем JSON
- Запросы обычно выполняются быстрее.
Минусы
- Синтаксис большесложный, не такой прямой
Надеюсь, это поможет, удачи.