【问题标题】:BigQuery: How to merge HLL Sketches over a window function? (Count distinct values over a rolling window)BigQuery:如何在窗口函数上合并 HLL 草图? (计算滚动窗口上的不同值)
【发布时间】:2019-07-15 21:14:08
【问题描述】:

相关表架构示例:

+---------------------------+-------------------+
| 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_id 连接到一个数组,然后计算其中的不同值。

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 草图以小得多的值表示不同的 ID,这样内存问题就不会那么严重了。我以为我的问题已经解决了,但是在运行以下命令时出现错误:错误只是“不支持函数 MERGE_PARTIAL”。我也尝试了 MERGE 并得到了同样的错误。它仅在使用窗口功能时发生。为每天的价值创建草图效果很好。

我通读了 BigQuery 标准 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 

"Image of the error - Function MERGE_PARTIAL is not supported"

任何想法为什么会发生此错误或如何调整?

【问题讨论】:

  • 您的费率很低。对 SO 很重要 - 您可以使用已发布答案左侧投票下方的勾号 mark accepted answer。请参阅meta.stackexchange.com/questions/5234/… 了解为什么它很重要!对vote on answer 也很重要。投票选出有帮助的答案。 ...您可以检查当有人回答您的问题时该怎么做 - stackoverflow.com/help/someone-answers。遵循这些简单的规则,您可以提高自己的声誉分数,同时让我们有动力回答您的问题:o) 请考虑!

标签: sql google-bigquery hyperloglog


【解决方案1】:

以下是 BigQuery 标准 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
  )
)

您可以使用 [totally] 虚拟数据进行测试、使用,如下例所示

#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    

【讨论】:

  • 不错的答案...我喜欢WITH `project.dataset.table` AS。将使用
【解决方案2】:

结合HLL_COUNT.INITHLL_COUNT.MERGE。此解决方案使用 GENERATE_ARRAY(1, 90) 而不是 OVER 的 90 天交叉连接。

#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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-10-10
    • 2016-10-23
    • 2018-02-02
    • 2018-07-07
    • 1970-01-01
    • 2012-11-08
    • 2013-01-15
    • 2012-12-27
    相关资源
    最近更新 更多