BigQuery - вложенные операции внутри раздела для объединения последовательных записей из таблицы с 17 миллиардами записей. - PullRequest
0 голосов
/ 12 декабря 2018

Я довольно новичок в 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, который:

  1. Запрашивает BQ для всех данных для одного идентификатора
  2. Сортирует данные по отметке времени
  3. ИспользуетФункция diff в "Location" определяет, когда она изменяется
  4. Использует cumsum (), чтобы пометить все элементы для каждой группы пингов одним и тем же значением.
  5. использует 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 требует, чтобы все данные были размещены на одном узле.

Вопросы

  1. Возможно лииспользовать приведенный выше подход SQL, но вкладывать всю логику в оператор раздела?В принципе, разделите по user_id и выполните все упорядочения, cumum и т. Д. Внутри этого раздела?
  2. Есть ли лучший подход к решению этой проблемы?

Я ценю любой ивсе входные.Я в полном недоумении относительно лучшего способа решения этой проблемы и чувствую выход из своей глубины.

Ответы [ 2 ]

0 голосов
/ 12 декабря 2018

Попробуйте версию ниже (BigQuery Standard SQL)

#standardSQL
SELECT 
  id, 
  MIN(timestamp) AS first_ping, 
  MAX(timestamp) AS last_ping, 
  ANY_VALUE(location) AS location
FROM (
  SELECT id, timestamp, location,
    COUNTIF(flag) OVER(PARTITION BY id ORDER BY timestamp) grp
  FROM (
    SELECT *, 
      location != LAG(location) OVER(PARTITION BY id ORDER BY timestamp) flag
    FROM `project.dataset.ping_table`
  )
)
GROUP BY id, grp
0 голосов
/ 12 декабря 2018

cumulativeSum должно быть рассчитано с использованием совокупной суммы вместо неэкви-объединения:

WITH visitWithIsChange AS 
(select
   *,
    CASE 
     WHEN (LAG(location,1,'') 
           OVER (PARTITION BY user_id ORDER BY timestamp)) = location
           THEN 0
           ELSE 1
     END ischange
 FROM `ping_table`
 -- I don't now about BigQuery, but why do you need this?
 --ORDER BY user_id, timestamp
 ),
 visitsWithcumsum AS (
   SELECT 
      *,
      SUM(ischange)
      OVER (PARTITION BY user_id
            ORDER BY timestamp
            ROWS UNBOUNDED PREDECING) AS cumulativeSum 
   FROM visitWithIsChange  
)
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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...