#sql #google-cloud-platform #google-bigquery
#sql #google-облачная платформа #google-bigquery
Вопрос:
Предположим, у меня есть следующая запись в BQ:
id name age timestamp
1 "tom" 20 2019-01-01
Затем я выполняю два «обновления» для этой записи, используя streaming API для «добавления» дополнительных данных — https://cloud.google.com/bigquery/streaming-data-into-bigquery . В основном это делается для того, чтобы обойти квоту обновления, которую применяет BQ (и у нас есть приложение с высокой скоростью записи).
Затем я добавляю два изменения в таблицу, одно обновление, которое просто изменяет name
, а затем одно обновление, которое просто изменяет age
. Вот три записи после обновлений:
id name age timestamp
1 "tom" 20 2019-01-01
1 "Tom" null 2019-02-01
1 null 21 2019-03-03
Затем я хочу запросить эту запись, чтобы получить самую «актуальную» информацию. Вот как я начал:
SELECT id, **name**, **age**,max(timestamp)
FROM table
GROUP BY id
-- 1,"Tom",21,2019-03-03
Как мне получить правильное имя и возраст здесь? Обратите внимание, что для записи могут быть тысячи обновлений, поэтому я не хочу писать 1000 операторов case, если это вообще возможно.
По разным другим причинам у меня обычно не будет всех данных строки за один раз, у меня будет только RowID FieldName FieldValue .
Я полагаю, что план B здесь состоит в том, чтобы выполнить запрос для получения текущих данных, а затем добавить мои изменения, чтобы вставить новую строку, но я надеюсь, что есть способ сделать это за один раз, не выполняя два запроса.
Ответ №1:
Ниже приведен стандартный SQL BigQuery
#standardSQL
SELECT id,
ARRAY_AGG(name IGNORE NULLS ORDER BY ts DESC LIMIT 1)[OFFSET(0)] name,
ARRAY_AGG(age IGNORE NULLS ORDER BY ts DESC LIMIT 1)[OFFSET(0)] age,
MAX(ts) ts
FROM `project.dataset.table`
GROUP BY id
Вы можете протестировать, поиграть с приведенным выше примером данных из вашего вопроса, как в примере ниже
#standardSQL
WITH `project.dataset.table` AS (
SELECT 1 id, "tom" name, 20 age, DATE '2019-01-01' ts UNION ALL
SELECT 1, "Tom", NULL, '2019-02-01' UNION ALL
SELECT 1, NULL, 21, '2019-03-03'
)
SELECT id,
ARRAY_AGG(name IGNORE NULLS ORDER BY ts DESC LIMIT 1)[OFFSET(0)] name,
ARRAY_AGG(age IGNORE NULLS ORDER BY ts DESC LIMIT 1)[OFFSET(0)] age,
MAX(ts) ts
FROM `project.dataset.table`
GROUP BY id
с результатом
Row id name age ts
1 1 Tom 21 2019-03-03
Комментарии:
1. Здесь я столкнулся с одной мыслью, которой у меня не было, когда я изначально опубликовал вопрос: что, если для обновляемого значения установлено значение null? Например, если возраст меняется с 20 на null? Как бы вы предложили разобраться с подобным случаем?
2. вы можете использовать
RESPECT NULLS
вместоIGNORE NULLS
— в этом случае имя будет удалено,NULL
например
Ответ №2:
Это классический случай применения аналитических функций в стандартном SQL.
Вот как вы можете добиться своих результатов:
select id, name, age from (
select id, name, age, ts, rank() over (partition by id order by ts desc) rnk
from `yourdataset.yourtable`
)
where rnk = 1
Это позволит сгруппировать ваши записи на основе id
и выбрать самую последнюю ts
(с указанием записи, добавленной последней для данного id
).
Комментарии:
1. как бы производительность этого сравнивалась с приведенным выше ответом? Кроме того, я вполне уверен, что это может привести к ошибке нехватки памяти из bq при разделении по идентификатору, не так ли?