Sql 在BigQuery中滚动90天活跃用户,提高性能(DAU/MAU/WAU)

Sql 在BigQuery中滚动90天活跃用户,提高性能(DAU/MAU/WAU),sql,google-bigquery,bigquery-standard-sql,Sql,Google Bigquery,Bigquery Standard Sql,我正在尝试获取某个特定日期的独特事件的数量,滚动90/30/7天。我使用下面的查询在有限的行上进行了此操作,但是对于大型数据集,我从聚合字符串中得到内存错误,这将变得非常庞大 我正在寻找一种更有效的方法来达到同样的效果 表如下所示: +---+------------+-------------+ | | date | userid | +---+------------+-------------+ | 1 | 2013-05-14 | xxxxx | |

我正在尝试获取某个特定日期的独特事件的数量,滚动90/30/7天。我使用下面的查询在有限的行上进行了此操作,但是对于大型数据集,我从聚合字符串中得到内存错误,这将变得非常庞大

我正在寻找一种更有效的方法来达到同样的效果

表如下所示:

+---+------------+-------------+
|   |     date   |     userid  |
+---+------------+-------------+
| 1 | 2013-05-14 | xxxxx       |
| 2 | 2017-03-14 | xxxxx       |
| 3 | 2018-01-24 | xxxxx       |
| 4 | 2013-03-21 | xxxxx       |
| 5 | 2014-03-19 | xxxxx       |
| 6 | 2015-09-03 | xxxxx       |
| 7 | 2014-02-06 | xxxxx       |
| 8 | 2014-10-30 | xxxxx       |
| ..| ...        | ...         |
+---+------------+-------------+
所需结果的格式:

+---+------------+---------------------------------------------+
|   |     date   | active_users_7_days | active_users_90_days  |
+---+------------+---------------------------------------------+
| 1 | 2013-05-14 | 1240                | 34339                 |
| 2 | 2017-03-14 | 4334                | 54343                 |
| 3 | 2018-01-24 | .....               | .....                 |
| 4 | 2013-03-21 | .....               | .....                 |
| 5 | 2014-03-19 | .....               | .....                 |
| 6 | 2015-09-03 | .....               | .....                 |
| 7 | 2014-02-06 | .....               | .....                 |
| 8 | 2014-10-30 | .....               | .....                 |
| ..| ...        | .....               | .....                 |
+---+------------+---------------------------------------------+
我的查询如下所示:

#standardSQL
    WITH
      T1 AS(
      SELECT
        date,
        STRING_AGG(DISTINCT userid) AS IDs
      FROM
        `consumer.events`
      GROUP BY
        date ),
      T2 AS(
      SELECT
        date,
        STRING_AGG(IDs) OVER(ORDER BY UNIX_DATE(date) RANGE BETWEEN 90 PRECEDING
          AND CURRENT ROW) AS IDs
      FROM
        T1 )
    SELECT
      date,
      (
      SELECT
        COUNT(DISTINCT (userid))
      FROM
        UNNEST(SPLIT(IDs)) AS userid) AS NinetyDays
    FROM
      T2

您可以合计日期并计算总和。什么是聚合?以最近的日期为例:

select count(*) as num_users,
       sum(case when date > datediff(current_date, interval -30 day) then 1 else 0 end) as num_users_30days,
       sum(case when date > datediff(current_date, interval -60 day) then 1 else 0 end) as num_users_60days,
       sum(case when date > datediff(current_date, interval -90 day) then 1 else 0 end) as num_users_90days
from (select user_id, max(date) as max(date)
      from `consumer.events` e
      group by user_id
     ) e;
如果用户的最新日期在期间内,则应统计该用户


通过在子查询中使用
where
子句,您可以在特定日期获得该“截止日期”。

您可以聚合日期并进行求和。什么是聚合?以最近的日期为例:

select count(*) as num_users,
       sum(case when date > datediff(current_date, interval -30 day) then 1 else 0 end) as num_users_30days,
       sum(case when date > datediff(current_date, interval -60 day) then 1 else 0 end) as num_users_60days,
       sum(case when date > datediff(current_date, interval -90 day) then 1 else 0 end) as num_users_90days
from (select user_id, max(date) as max(date)
      from `consumer.events` e
      group by user_id
     ) e;
如果用户的最新日期在期间内,则应统计该用户


通过在子查询中使用
where
子句,您可以在特定日期获得此“截止日期”。

计算唯一用户需要大量资源,如果您希望通过滚动窗口获得结果,则需要更多资源。对于可扩展的解决方案,请查看近似算法,如HLL++:

对于精确计数,这将起作用(但随着窗口变大,速度会变慢):

#标准SQL
选择日期子项(日期、间隔/天)日期
,计数(不同所有者\用户\ id)唯一\ 90 \天\用户

,COUNT(DISTINCT IF)(i计算唯一用户需要大量资源,如果您希望在滚动窗口上获得结果,则需要更多资源。要获得可扩展的解决方案,请查看类似HLL++的近似算法:

对于精确计数,这将起作用(但随着窗口变大,速度会变慢):

#标准SQL
选择日期子项(日期、间隔/天)日期
,计数(不同所有者\用户\ id)唯一\ 90 \天\用户


,COUNT(DISTINCT IF)(我为什么要一个大的
字符串\u AGG(DISTINCT userid)
?@FelipeHoffa我想我需要按日期分组的不同用户ID。你还有其他更有效的方法来实现这个结果吗?@Frithiof我想Felipe想问的是,你需要显示实际的ID还是唯一的计数就够了?除非你真的需要看到,否则导致内存错误的是字符串的聚合然后,它们只返回一个计数。@BenP是的,字符串的聚合导致了错误。我不需要查看实际的ID。也许我速度慢了,但是如果不聚合它们,我如何计算日期范围内的不同ID?为什么需要大量的
字符串\u AGG(不同的用户ID)
?@FelipeHoffa我想我需要按日期分组的不同用户ID。你还有其他更有效的方法来实现这个结果吗?@Frithiof我想Felipe想问的是,你需要显示实际的ID还是唯一的计数就够了?除非你真的需要看到,否则导致内存错误的是字符串的聚合然后,它们只返回一个计数。@BenP是的,字符串的聚合导致了错误。我不需要查看实际的ID。也许我速度慢了,但如何在不聚合它们的情况下计算日期范围内的不同ID?谢谢Gordon,但我需要表中所有日期的结果。我已编辑我的问题的格式是我想要的。谢谢Gordon,但我需要表格中所有日期的结果。我已经用我想要的格式编辑了我的问题。谢谢Felipe,这就是我要找的。我也会查看HLL++的。但是,在完整日期的前90天和最后90天,结果将不准确e、 我的查询将更正确,因为它每行回滚90天。有什么方法可以做到这一点吗?简单:删除前90天的结果,或将范围扩大90天。如果我错了,请纠正我,但如果我将范围扩大90天,我将在将来填充没有意义的日期。我会为此使用限制和偏移吗?重新设置结果是最新的,不要使用
date\u ADD
而不是
date\u SUB
。它会给出完全相同的结果,不同的是它会列出90天内的最后一天,而不是第一天。但不管怎样,你都会得到最新的90天。谢谢你Felipe,这是我要找的。我也会查看HLL++的。结果会很好在完整日期范围的前90天和最后90天内,我们可能会不准确。我的查询将更准确,因为它每行回滚90天。有什么方法可以做到这一点吗?简单:删除前90天的结果,或将范围扩大90天。如果我错了,请纠正我,但如果我将范围扩大90天,我将在没有意义的未来。我会为此使用限制和偏移吗?要使结果最新,请执行
date\u ADD
而不是
date\u SUB
。它会给出完全相同的结果,不同的是它列出了90天期间的最后一天,而不是第一天。但无论如何,您都会得到最新的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
#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
 , COUNT(*) window_days
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
HAVING window_days=90
ORDER BY date_grp