查询一段时间内的 DAU/MAU(每天)

Posted

技术标签:

【中文标题】查询一段时间内的 DAU/MAU(每天)【英文标题】:Querying DAU/MAU over time (daily) 【发布时间】:2014-06-30 16:04:24 【问题描述】:

我有一个包含 user_id 和日期列的每日会话表。我想每天绘制 DAU/MAU(日活跃用户/月活跃用户)。例如:

Date         MAU      DAU     DAU/MAU
2014-06-01   20,000   5,000   20%
2014-06-02   21,000   4,000   19%
2014-06-03   20,050   3,050   17%
...          ...      ...     ...

计算每日活跃度很容易计算,但计算每月活跃度,例如在日期 30 天内登录的用户数量会导致问题。如果没有每天的左连接,这是如何实现的?

编辑:我正在使用 Postgres。

【问题讨论】:

你使用的是什么数据库,mysql 还是 Postgres? 【参考方案1】:

假设您每天都有值,您可以使用子查询和range between 获得总计数:

with dau as (
      select date, count(userid) as dau
      from dailysessions ds
      group by date
     )
select date, dau,
       sum(dau) over (order by date rows between -29 preceding and current row) as mau
from dau;

不幸的是,我认为您需要不同的用户,而不仅仅是用户数量。这使问题变得更加困难,尤其是因为 Postgres 不支持 count(distinct) 作为窗口函数。

我认为您必须为此进行某种自我加入。这是一种方法:

with dau as (
      select date, count(distinct userid) as dau
      from dailysessions ds
      group by date
     )
select date, dau,
       (select count(distinct user_id)
        from dailysessions ds
        where ds.date between date - 29 * interval '1 day' and date
       ) as mau
from dau;

【讨论】:

当我运行此查询多天时,结果集中的mau 列不会改变,理想情况下它应该改变,因为mau 每天应该不同。有关如何解决此问题的任何指示? @Patthebug 。 . .尝试使用示例数据和所需结果提出一个新问题。 这是新问题:***.com/questions/53839230/… @GordonLinoff,像往常一样,非常优雅的解决方案,谢谢! 这个解决方案的前提忽略了MAU不是一个月DAU的总和。否则,如果您在一个月内每天都有同一个用户,您的 MAU 将为 30,而实际上应该为 1。【参考方案2】:

这个使用 COUNT DISTINCT 来获得滚动的 30 天 DAU/MAU:

(计算 reddit 在 BigQuery 中的用户参与度 - 但 SQL 标准足以用于其他数据库)

SELECT day, dau, mau, INTEGER(100*dau/mau) daumau
FROM (
  SELECT day, EXACT_COUNT_DISTINCT(author) dau, FIRST(mau) mau
  FROM (
    SELECT DATE(SEC_TO_TIMESTAMP(created_utc)) day, author
    FROM [fh-bigquery:reddit_comments.2015_09]
    WHERE subreddit='AskReddit') a
  JOIN (
    SELECT stopday, EXACT_COUNT_DISTINCT(author) mau
    FROM (SELECT created_utc, subreddit, author FROM [fh-bigquery:reddit_comments.2015_09], [fh-bigquery:reddit_comments.2015_08]) a
    CROSS JOIN (
      SELECT DATE(SEC_TO_TIMESTAMP(created_utc)) stopday
      FROM [fh-bigquery:reddit_comments.2015_09]
      GROUP BY 1
    ) b
    WHERE subreddit='AskReddit'
    AND SEC_TO_TIMESTAMP(created_utc) BETWEEN DATE_ADD(stopday, -30, 'day') AND TIMESTAMP(stopday)
    GROUP BY 1
  ) b
  ON a.day=b.stopday
  GROUP BY 1
)
ORDER BY 1

我在How to calculate DAU/MAU with BigQuery (engagement)走得更远

【讨论】:

【参考方案3】:

我已经在my blog 上写过这个。

如您所见,DAU 很简单。您可以通过首先创建一个带有布尔值的视图来解决 MAU,当用户激活和取消激活时,如下所示:

CREATE OR REPLACE VIEW "vw_login" AS 
 SELECT *
    , LEAST (LEAD("date") OVER w, "date" + 30) AS "activeExpiry"
    , CASE WHEN LAG("date") OVER w IS NULL THEN true ELSE false AS "activated"
    , CASE
 WHEN LEAD("date") OVER w IS NULL THEN true
 WHEN LEAD("date") OVER w - "date" > 30 THEN true
 ELSE false
 END AS "churned"
    , CASE
 WHEN LAG("date") OVER w IS NULL THEN false
 WHEN "date" - LAG("date") OVER w <= 30 THEN false
 WHEN row_number() OVER w > 1 THEN true
 ELSE false
 END AS "resurrected"
   FROM "login"
   WINDOW w AS (PARTITION BY "user_id" ORDER BY "date")

这会在每个用户每天活跃、流失和重新激活时创建布尔值。

然后每天做同样的汇总:

CREATE OR REPLACE VIEW "vw_activity" AS
SELECT 
    SUM("activated"::int) "activated"
  , SUM("churned"::int) "churned"
  , SUM("resurrected"::int) "resurrected"
  , "date"
  FROM "vw_login"
  GROUP BY "date"
  ;

最后通过计算列的累积总和来计算活动 MAU 的运行总数。您需要加入 vw_activity 两次,因为第二次加入到用户变为非活动的那一天(即自上次登录后 30 天)。

为了确保所有日期都存在于您的数据集中,我添加了一个日期系列。您也可以不使用它,但您可能会在数据集中跳过几天。

SELECT
 d."date"
 , SUM(COALESCE(a.activated::int,0)
   - COALESCE(a2.churned::int,0)
   + COALESCE(a.resurrected::int,0)) OVER w
 , d."date", a."activated", a2."churned", a."resurrected" FROM
 generate_series('2010-01-01'::date, CURRENT_DATE, '1 day'::interval) d
 LEFT OUTER JOIN vw_activity a ON d."date" = a."date"
 LEFT OUTER JOIN vw_activity a2 ON d."date" = (a2."date" + INTERVAL '30 days')::date
 WINDOW w AS (ORDER BY d."date") ORDER BY d."date";

您当然可以在单个查询中执行此操作,但这有助于更好地理解结构。

【讨论】:

【参考方案4】:

您没有向我们展示您的完整表定义,但可能是这样的:

select date,
       count(*) over (partition by date_trunc('day', date) order by date) as dau,
       count(*) over (partition by date_trunc('month', date) order by date) as mau
from sessions
order by date;

要在不重复窗口函数的情况下获得百分比,只需将其包装在派生表中即可:

select date, 
       dau,
       mau,
       dau::numeric / (case when mau = 0 then null else mau end) as pct
from (
    select date,
           count(*) over (partition by date_trunc('day', date) order by date) as dau,
           count(*) over (partition by date_trunc('month', date) order by date) as mau
    from sessions
) t
order by date;

这是一个示例输出:

postgres=> 从会话中选择 *; 会话日期 |用户身份 --------------+---------- 2014-05-01 | 1 2014-05-01 | 2 2014-05-01 | 3 2014-05-02 | 1 2014-05-02 | 2 2014-05-02 | 3 2014-05-02 | 4 2014-05-02 | 5 2014-06-01 | 1 2014-06-01 | 2 2014-06-01 | 3 2014-06-02 | 1 2014-06-02 | 2 2014-06-02 | 3 2014-06-02 | 4 2014-06-03 | 1 2014-06-03 | 2 2014-06-03 | 3 2014-06-03 | 4 2014-06-03 | 5 (20 行) postgres=> 选择 session_date, postgres-> dau, postgres-> 茂, postgres-> round(dau::numeric / (当 mau = 0 然后为 null 否则 mau end),2) as pct postgres-> 从 ( postgres(> 选择 session_date, postgres(> count(*) over (partition by date_trunc('day', session_date) order by session_date) 作为 dau, postgres(> count(*) over (partition by date_trunc('month', session_date) order by session_date) as mau postgres(> 来自会话 postgres(>) t postgres-> 按 session_date 排序; 会话日期 |头|茂 |百分比 --------------+------+------+------ 2014-05-01 | 3 | 3 | 1.00 2014-05-01 | 3 | 3 | 1.00 2014-05-01 | 3 | 3 | 1.00 2014-05-02 | 5 | 8 | 0.63 2014-05-02 | 5 | 8 | 0.63 2014-05-02 | 5 | 8 | 0.63 2014-05-02 | 5 | 8 | 0.63 2014-05-02 | 5 | 8 | 0.63 2014-06-01 | 3 | 3 | 1.00 2014-06-01 | 3 | 3 | 1.00 2014-06-01 | 3 | 3 | 1.00 2014-06-02 | 4 | 7 | 0.57 2014-06-02 | 4 | 7 | 0.57 2014-06-02 | 4 | 7 | 0.57 2014-06-02 | 4 | 7 | 0.57 2014-06-03 | 5 | 12 | 0.42 2014-06-03 | 5 | 12 | 0.42 2014-06-03 | 5 | 12 | 0.42 2014-06-03 | 5 | 12 | 0.42 2014-06-03 | 5 | 12 | 0.42 (20 行) postgres=>

【讨论】:

看起来不错 - 问题:MAU 是日历月还是每天的前一个月?理想情况是当天的前一个月。 我已经运行了这个查询,但它不起作用。 MAU从月初的0增加到月底的累计用户总数。此外,包装器需要按日期、dau、mau 分组。 @DavidBailey:那么您需要提供更多详细信息——尤其是表结构和更多示例数据。不,包装器不需要分组,因为我使用的是生成累积计数的窗口函数。不幸的是,SQL Fiddle 现在不起作用,因为那时我会提供一个“实时”示例。我已经添加了一个 psql 会话的脚本,以向您展示我的示例表。 您假设的数据结构是正确的 :) 在实时示例中,每月活跃用户为 5 月 1 日为 3,5 月 2 日为 8,6 月 1 日为 3。目前尚不清楚这代表什么...... 好吧,sessions 表中有 5 月 1 日和 5 月 2 日 5 日的三个条目。所以结果显示 5 月 1 日的 DAU 为 3,5 月 2 日的 DAU 为 5,但 MAU 进行了累积计数,这意味着 5 月 2 日有 8 个会话。

以上是关于查询一段时间内的 DAU/MAU(每天)的主要内容,如果未能解决你的问题,请参考以下文章

段的 DAU/MAU 计算

如何使用 BigQuery(参与度)计算 DAU/MAU

REDIS05_HyperLogLog的概述基本命令UVPVDAUMAU首页UV如何进行统计处理

REDIS05_HyperLogLog的概述基本命令UVPVDAUMAU首页UV如何进行统计处理

请问,在mysql中如何查询每天固定时间段内的数据??

在mysql中如何查询每天固定时间段内的数据?