BigQuery: Как объединить эскизы HLL в оконную функцию?(Посчитайте разные значения через скользящее окно) - PullRequest
0 голосов
/ 21 февраля 2019

Пример соответствующей схемы таблицы:

+---------------------------+-------------------+
| activity_date - TIMESTAMP | user_id - STRING  |
+---------------------------+-------------------+
| 2017-02-22 17:36:08 UTC   | fake_id_i24385787 |
+---------------------------+-------------------+
| 2017-02-22 04:27:08 UTC   | fake_id_234885747 |
+---------------------------+-------------------+
| 2017-02-22 08:36:08 UTC   | fake_id_i24385787 |
+---------------------------+-------------------+

Мне нужно подсчитывать активных отдельных пользователей по большому набору данных в течение скользящего периода времени (90 дней), и я сталкиваюсь с проблемами из-за размеранабор данных.

Сначала я попытался использовать оконную функцию, подобную ответу здесь.https://stackoverflow.com/a/27574474

WITH
  daily AS (
  SELECT
    DATE(activity_date) day,
    user_id
  FROM
    `fake-table`)
SELECT
  day,
  SUM(APPROX_COUNT_DISTINCT(user_id)) OVER (ORDER BY day ROWS BETWEEN 89 PRECEDING AND CURRENT ROW) ninty_day_window_apprx
FROM
  daily
GROUP BY
  1
ORDER BY
  1 DESC

Однако это привело к получению различного числа пользователей в день, а затем суммированию их - но различия могут дублироваться в окне, если они появляются несколько раз.Так что это не совсем точный показатель для отдельных пользователей за 90 дней.

Следующее, что я попробовал, - это использовать следующее решение https://stackoverflow.com/a/47659590 - объединение всех отдельных user_ids для каждого окна в массиви затем подсчитываем различия в этом.

WITH daily AS (
  SELECT date(activity_date) day, STRING_AGG(DISTINCT user_id) users
  FROM `fake-table`  
  GROUP BY day
), temp2 AS (
  SELECT
    day, 
    STRING_AGG(users) OVER(ORDER BY UNIX_DATE(day) RANGE BETWEEN 89 PRECEDING AND CURRENT ROW) users
  FROM daily
)

SELECT day, 
  (SELECT APPROX_COUNT_DISTINCT(id) FROM UNNEST(SPLIT(users)) AS id) Unique90Days
FROM temp2

order by 1 desc

Однако это быстро исчерпало память с чем-либо большим.

Далее было использовать эскиз HLL для представления различных идентификаторов в гораздо меньшем размере.значение, поэтому память будет меньше проблем.Я думал, что мои проблемы были решены, но я получаю сообщение об ошибке при выполнении следующего: Ошибка просто «Функция MERGE_PARTIAL не поддерживается».Я тоже попробовал с MERGE и получил ту же ошибку.Это происходит только при использовании оконной функции.Создание эскизов для ценности каждого дня прекрасно работает.

Я прочитал документацию по BigQuery Standard SQL и не вижу ничего о HLL_COUNT.MERGE_PARTIAL и HLL_COUNT.MERGE с оконными функциями.Предположительно, это должно взять 90 эскизов и объединить их в один эскиз HLL, представляющий различные значения между 90 исходными эскизами?

WITH
  daily AS (
  SELECT
    DATE(activity_date) day,
    HLL_COUNT.INIT(user_id) sketch
  FROM
    `fake-table`
  GROUP BY
    1
  ORDER BY
    1 DESC),

  rolling AS (
  SELECT
    day,
    HLL_COUNT.MERGE_PARTIAL(sketch) OVER (ORDER BY UNIX_DATE(day) RANGE BETWEEN 89 PRECEDING AND CURRENT ROW) rolling_sketch
    FROM daily)

SELECT
  day,
  HLL_COUNT.EXTRACT(rolling_sketch)
FROM
  rolling
ORDER BY
  1 

«Изображение ошибки - функция MERGE_PARTIAL не поддерживается»

Есть идеи, почему возникает эта ошибка или как ее исправить?

Ответы [ 2 ]

0 голосов
/ 22 февраля 2019

Ниже для BigQuery Standard SQL и делает именно то, что вы хотите с использованием оконной функции

#standardSQL
SELECT day,
  (SELECT HLL_COUNT.MERGE(sketch) FROM UNNEST(rolling_sketch_arr) sketch)  rolling_sketch
FROM (
  SELECT day, 
    ARRAY_AGG(ids_sketch) OVER(ORDER BY UNIX_DATE(day) RANGE BETWEEN 89 PRECEDING AND CURRENT ROW) rolling_sketch_arr 
  FROM (
    SELECT day, HLL_COUNT.INIT(id) ids_sketch
    FROM `project.dataset.table`
    GROUP BY day
  )
)

Вы можете протестировать, поиграть с выше, используя [полностью] фиктивные данные, как в примере ниже

#standardSQL
WITH `project.dataset.table` AS (
  SELECT 1 id, DATE '2019-01-01' day UNION ALL
  SELECT 2, '2019-01-01' UNION ALL
  SELECT 3, '2019-01-01' UNION ALL
  SELECT 1, '2019-01-02' UNION ALL
  SELECT 4, '2019-01-02' UNION ALL
  SELECT 2, '2019-01-03' UNION ALL
  SELECT 3, '2019-01-03' UNION ALL
  SELECT 4, '2019-01-03' UNION ALL
  SELECT 5, '2019-01-03' UNION ALL
  SELECT 1, '2019-01-04' UNION ALL
  SELECT 4, '2019-01-04' UNION ALL
  SELECT 2, '2019-01-05' UNION ALL
  SELECT 3, '2019-01-05' UNION ALL
  SELECT 5, '2019-01-05' UNION ALL
  SELECT 6, '2019-01-05' 
)
SELECT day,
  (SELECT HLL_COUNT.MERGE(sketch) FROM UNNEST(rolling_sketch_arr) sketch)  rolling_sketch
FROM (
  SELECT day, 
    ARRAY_AGG(ids_sketch) OVER(ORDER BY UNIX_DATE(day) RANGE BETWEEN 2 PRECEDING AND CURRENT ROW) rolling_sketch_arr 
  FROM (
    SELECT day, HLL_COUNT.INIT(id) ids_sketch
    FROM `project.dataset.table`
    GROUP BY day
  )
)
-- ORDER BY day

с результатом

Row day         rolling_sketch   
1   2019-01-01  3    
2   2019-01-02  4    
3   2019-01-03  5    
4   2019-01-04  5    
5   2019-01-05  6    
0 голосов
/ 22 февраля 2019

Объединение HLL_COUNT.INIT и HLL_COUNT.MERGE.Это решение использует 90-дневное перекрестное соединение с GENERATE_ARRAY(1, 90) вместо OVER.

#standardSQL
SELECT DATE_SUB(date, INTERVAL i DAY) date_grp
 , HLL_COUNT.MERGE(sketch) unique_90_day_users
 , HLL_COUNT.MERGE(DISTINCT IF(i<31,sketch,null)) unique_30_day_users
 , HLL_COUNT.MERGE(DISTINCT IF(i<8,sketch,null)) unique_7_day_users
FROM (
  SELECT DATE(creation_date) date, HLL_COUNT.INIT(owner_user_id) sketch
  FROM `bigquery-public-data.stackoverflow.posts_questions` 
  WHERE EXTRACT(YEAR FROM creation_date)=2017
  GROUP BY 1
), UNNEST(GENERATE_ARRAY(1, 90)) i
GROUP BY 1
ORDER BY date_grp

enter image description here

...