Я довольно новичок в SQL и BigQuery, и вот уже неделю в течение недели пытаюсь найти реальное решение этой проблемы.Два решения, которые у меня есть, не масштабируются.
Фон
Имеют таблицу BigQuery с 17 миллиардами записей.Каждая запись представляет один пинг устройства.Каждая запись содержит метку времени, идентификатор для идентификации пользователя и название местоположения, получившего пинг.
Возьмите эту таблицу данных, разделите ее по идентификатору и отсортируйте по метке времени.Тогда у вас есть хронологически упорядоченный набор пингов.У пользователя может быть 1 пинг до местоположения A, затем 7 до местоположения B, затем от 2 до местоположения C и еще 2 до A.
ID timestamp Location
ABC123 2017-10-12 10:20:37 A
ABC123 2017-10-12 11:15:21 B
ABC123 2017-10-12 11:21:47 B
ABC123 2017-10-12 11:25:05 B
ABC123 2017-10-12 11:32:12 B
ABC123 2017-10-12 11:36:24 B
ABC123 2017-10-12 11:47:13 B
ABC123 2017-10-12 11:59:08 B
ABC123 2017-10-12 12:04:42 C
ABC123 2017-10-12 17:04:52 C
ABC123 2017-10-12 19:15:37 A
ABC123 2017-10-12 19:18:37 A
Что я хотел бы сделать, это взять эту таблицу ипроизвести новый с одним рядом за "поездку".Где поездка - это группа последовательных пингов, со столбцами «first_ping» и «last_ping».Если поездка состоит из 1 пинга, эта временная метка является первым и последним пингом.
ID first_ping last_ping Location
ABC123 2017-10-12 10:20:37 2017-10-12 10:20:37 A
ABC123 2017-10-12 11:15:21 2017-10-12 11:59:08 B
ABC123 2017-10-12 12:04:42 2017-10-12 17:04:52 C
ABC123 2017-10-12 19:15:37 2017-10-12 19:18:37 A
Попытки решения
Python
Я никогда не работал с такими большими данными, и я всегда работал с Python.Итак, моей первой попыткой решения был сценарий Python, который:
- Запрашивает BQ для всех данных для одного идентификатора
- Сортирует данные по отметке времени
- ИспользуетФункция diff в "Location" определяет, когда она изменяется
- Использует cumsum (), чтобы пометить все элементы для каждой группы пингов одним и тем же значением.
- использует df.groupby () oncumsum (), чтобы получить по одной строке на запись, а first () и last (), чтобы получить значения first_ping и last_ping.
Это решение создает нужный мне вывод, но неосуществимо для 17миллиард записей и 69 миллионов уникальных идентификаторов.Для каждого идентификатора требуется около 10 секунд, а время выполнения - около 190 тыс. Часов.
SQL
WITH visitWithIsChange AS
(select
*,
LAG(location,1,'') OVER (PARTITION BY user_id ORDER BY timestamp) previous,
CASE
WHEN (LAG(location,1,'')
OVER (PARTITION BY user_id ORDER BY timestamp)) = location
THEN 0
ELSE 1
END ischange
FROM `ping_table` ORDER BY user_id, timestamp),
visitsWithcumsum AS (
SELECT
t1.*,
SUM(t2.ischange) AS cumulativeSum
FROM visitWithIsChange t1
INNER JOIN
visitWithIsChange t2
ON
t1.local_timestamp >=t2.local_timestamp
AND
t1.user_id=t2.user_id
GROUP BY
t1.local_timestamp,
t1.user_id,
t1.chain_id,
t1.previous,
t1.isChange
ORDER BY user_id, timestamp
)
SELECT
MIN(timestamp) AS first_ping,
MAX(local_timestamp) AS last_ping,
user_id,
chain_id,
FROM visitsWithcumsum
GROUP BY
user_id,
cumulativeSum,
chain_id,
ORDER BY user_id, first_ping
Я знаю, что проблема с оператором SQL заключается виспользование ORDER BY вне перегородок.BigQuery выбрасывает ошибки, превышающие ресурс каждый раз, когда ORDER BY вызывается более чем на пару сотен тысяч строк.Насколько я понимаю, это происходит потому, что оператор ORDER BY требует, чтобы все данные были размещены на одном узле.
Вопросы
- Возможно лииспользовать приведенный выше подход SQL, но вкладывать всю логику в оператор раздела?В принципе, разделите по user_id и выполните все упорядочения, cumum и т. Д. Внутри этого раздела?
- Есть ли лучший подход к решению этой проблемы?
Я ценю любой ивсе входные.Я в полном недоумении относительно лучшего способа решения этой проблемы и чувствую выход из своей глубины.