Как отфильтровать расчеты хранения в BigQuery по определенным пользовательским событиям из Firebase - PullRequest
0 голосов
/ 11 марта 2019

Я сделал запрос, основанный на общем запросе: https://github.com/sagishporer/big-query-queries-for-firebase/wiki/Query:-Daily-retention, чтобы рассчитать удержание пользователя в BigQuery с данными, передаваемыми из Firebase.

Пока он работал, но по мере увеличения наборов данных он больше не может запускаться из-за следующей ошибки:

Превышены ресурсы во время выполнения запроса:Запрос не может быть выполнен в выделенной памяти.Пиковое использование: 129% от лимита.Основные потребители памяти: операции сортировки, используемые для аналитических предложений OVER (): 100%

Запрос следующий:

    SELECT
      install_date,
      SUM(CASE
          WHEN days_since_install = 0 THEN users
          ELSE 0 END) AS day_0,
      SUM(CASE
          WHEN days_since_install = 1 THEN users
          ELSE 0 END) AS day_1,
      SUM(CASE
          WHEN days_since_install = 2 THEN users
          ELSE 0 END) AS day_2,
      SUM(CASE
          WHEN days_since_install = 3 THEN users
          ELSE 0 END) AS day_3,
      SUM(CASE
          WHEN days_since_install = 4 THEN users
          ELSE 0 END) AS day_4,
      SUM(CASE
          WHEN days_since_install = 5 THEN users
          ELSE 0 END) AS day_5,
      SUM(CASE
          WHEN days_since_install = 6 THEN users
          ELSE 0 END) AS day_6,
      SUM(CASE
          WHEN days_since_install = 7 THEN users
          ELSE 0 END) AS day_7,
      SUM(CASE
          WHEN days_since_install = 8 THEN users
          ELSE 0 END) AS day_8,
      SUM(CASE
          WHEN days_since_install = 9 THEN users
          ELSE 0 END) AS day_9,
      SUM(CASE
          WHEN days_since_install = 10 THEN users
          ELSE 0 END) AS day_10,
      SUM(CASE
          WHEN days_since_install = 11 THEN users
          ELSE 0 END) AS day_11,
      SUM(CASE
          WHEN days_since_install = 12 THEN users
          ELSE 0 END) AS day_12,
      SUM(CASE
          WHEN days_since_install = 13 THEN users
          ELSE 0 END) AS day_13,
      SUM(CASE
          WHEN days_since_install = 14 THEN users
          ELSE 0 END) AS day_14,
      SUM(CASE
          WHEN days_since_install = 15 THEN users
          ELSE 0 END) AS day_15,
      SUM(CASE
          WHEN days_since_install = 16 THEN users
          ELSE 0 END) AS day_16,
      SUM(CASE
          WHEN days_since_install = 17 THEN users
          ELSE 0 END) AS day_17,
      SUM(CASE
          WHEN days_since_install = 18 THEN users
          ELSE 0 END) AS day_18,
      SUM(CASE
          WHEN days_since_install = 19 THEN users
          ELSE 0 END) AS day_19,
      SUM(CASE
          WHEN days_since_install = 20 THEN users
          ELSE 0 END) AS day_20,
      SUM(CASE
          WHEN days_since_install = 21 THEN users
          ELSE 0 END) AS day_21,
      SUM(CASE
          WHEN days_since_install = 22 THEN users
          ELSE 0 END) AS day_22,
      SUM(CASE
          WHEN days_since_install = 23 THEN users
          ELSE 0 END) AS day_23,
      SUM(CASE
          WHEN days_since_install = 24 THEN users
          ELSE 0 END) AS day_24,
      SUM(CASE
          WHEN days_since_install = 25 THEN users
          ELSE 0 END) AS day_25,
      SUM(CASE
          WHEN days_since_install = 26 THEN users
          ELSE 0 END) AS day_26,
      SUM(CASE
          WHEN days_since_install = 27 THEN users
          ELSE 0 END) AS day_27,
      SUM(CASE
          WHEN days_since_install = 28 THEN users
          ELSE 0 END) AS day_28,
      SUM(CASE
          WHEN days_since_install = 29 THEN users
          ELSE 0 END) AS day_29,
      SUM(CASE
          WHEN days_since_install = 30 THEN users
          ELSE 0 END) AS day_30
    FROM (
      SELECT
        DATE(TIMESTAMP_MICROS(user_first_touch_timestamp)) AS install_date,
        DATE(TIMESTAMP_MICROS(event_timestamp)) AS event_realdate,
        DATE_DIFF(DATE(TIMESTAMP_MICROS(event_timestamp)), DATE(TIMESTAMP_MICROS(user_first_touch_timestamp)), day) AS days_since_install,
        COUNT(DISTINCT user_pseudo_id) AS users
      FROM
        `dataset.events_2019*`
      WHERE
        event_name = 'user_engagement'
        AND user_pseudo_id NOT IN (
        SELECT
          user_pseudo_id
        FROM (
          SELECT
            MIN(global_session_id),
            user_pseudo_id,
            user_first_touch_timestamp,
            event_timestamp
          FROM (
            SELECT
              *,
              IF (previous_event='some_event'
                AND LAG(global_session_id,1)OVER (ORDER BY global_session_id, event_name)=global_session_id,
                LAG(global_session_id,1) OVER (ORDER BY global_session_id, event_name),
                NULL) AS match
            FROM (
              SELECT
                *,
                LAG(event_name,1) OVER (ORDER BY global_session_id, event_name) AS previous_event
              FROM (
                SELECT
                  global_session_id,
                  event_name,
                  user_first_touch_timestamp,
                  event_timestamp,
                  user_pseudo_id
                FROM (
                  SELECT
                    global_session_id,
                    event_name,
                    user_pseudo_id,
                    event_timestamp,
                    user_first_touch_timestamp,
                    IF (some_kill=1,
                      global_session_id,
                      NULL) AS session_some_kill,
                    IF (event_name='user_engagement',
                      global_session_id,
                      NULL) AS session
                  FROM (
                    SELECT
                      *,
                      CASE
                        WHEN event_params.key = 'Kills' AND event_params.value.int_value>0 THEN 1
                        ELSE 0
                      END AS some_kill,
                      SUM(is_new_session) OVER (ORDER BY user_pseudo_id, event_timestamp, event_name) AS global_session_id,
                      SUM(is_new_session) OVER (PARTITION BY user_pseudo_id ORDER BY event_timestamp) AS user_session_id
                    FROM (
                      SELECT
                        *,
                        CASE
                          WHEN event_timestamp - last_event >= (30 * 60 * 1000) OR last_event IS NULL THEN 1
                          ELSE 0
                        END AS is_new_session
                      FROM (
                        SELECT
                          user_pseudo_id,
                          event_timestamp,
                          event_name,
                          event_params,
                          user_first_touch_timestamp,
                          LAG(event_timestamp,1) OVER (PARTITION BY user_pseudo_id ORDER BY event_timestamp) AS last_event
                        FROM (
                          SELECT 
                          user_pseudo_id,
                          event_timestamp,
                          event_name,
                          event_params,
                          user_first_touch_timestamp
                          FROM `dataset.events_2019*`,
                          UNNEST (event_params) AS event_params)
                        ) last 
                      ) agg
                    )
                  )
                WHERE
                  session_some_kill IS NOT NULL
                  OR session IS NOT NULL
                GROUP BY
                  global_session_id,
                  event_name,
                  user_first_touch_timestamp,
                  event_timestamp,
                  user_pseudo_id
                ORDER BY
                  global_session_id ) ) )
          WHERE
            match IS NOT NULL
            AND event_timestamp-user_first_touch_timestamp<1.8e+9
          GROUP BY
            user_pseudo_id,
            user_first_touch_timestamp,
            event_timestamp))
      GROUP BY
        install_date,
        event_realdate,
        days_since_install )
    GROUP BY
      install_date
    HAVING
      day_0 > 0 /* Remove older dates - not enough data, you should also ignore the first record for partial data */
    ORDER BY
      install_date

Ответы [ 2 ]

0 голосов
/ 16 июля 2019

Мы делаем такие вещи за 2 прохода. Сначала мы вычисляем дни, в которые каждый пользователь активен, а затем делаем все необходимые вычисления.

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

SELECT
    user_id as userId,
    BIT_OR(1 << GREATEST(0, (DIV(event_timestamp, (24 * 60 * 60 * 1000000)) - DIV(user_first_touch_timestamp,(24 * 60 * 60 * 1000000)) ))) as DX,

Активные дни хранятся в виде битового поля с максимум 64 днями для INT64, а не для индивидуального расчета и хранения каждого отдельного дня. Вы можете добавить больше дней, по мере необходимости, 64 за INT64, смещая сдвиг. Запросы выполняются и экспортируются очень быстро.

Это UTC, при необходимости вы можете перейти на местное время.

Нам нужен GREATEST только потому, что мы группируем по user_id и используем привязку аккаунта, а когда пользователь удаляет, переустанавливает и связывает, пользователь получает еще один first_touch_timestamp, который новее старых событий.

Надеюсь, это поможет,

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

Попробуйте следующее:

  1. В предложении UNNEST добавьте WHERE, чтобы уменьшить размер возвращаемых записей, влияющих на производительность, например:

    SELECT 
      user_pseudo_id,
      event_timestamp,
      event_name,
      event_params,
      user_first_touch_timestamp
      FROM `analytics_185672896.events_2019*`,
      UNNEST (event_params) AS event_params)
    <b>event_name = 'user_engagement</b>
  2. Удалите ORDER BY во внутреннем SQL, чтобы избежать дополнительных вычислений там, где это не нужно, поскольку BQ потребуется получить все результаты и ЗАКАЗАТЬ, прежде чем переходить к следующему шагу в плане выполнения, см. Эту ссылку для получения дополнительной информации

...