You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

BigQuery窗口函数中计算月度累计去重活跃用户的问题

问题分析与解决方案

你遇到的核心问题是:BigQuery不允许在窗口函数的COUNT(DISTINCT)中搭配ORDER BY和RANGE/ROWS框架——这是BigQuery的语法限制,直接写COUNT(DISTINCT active_users) OVER (...)会报错,因为窗口函数中的去重计数无法和行范围筛选一起使用。

要实现「每月从月初到当前日的累计唯一活跃用户数」,我们需要换一种思路:先标记每个用户在当月的首次活跃记录,再通过窗口函数累加这些首次记录的数量,就能得到累计去重用户数。

下面是针对你完整查询的修改方案:

方案一:预计算用户当月首次活跃日期

with user_first_monthly_activity as (
  -- 先获取每个用户在每个月、sample、app_id下的首次活跃日期
  select 
    td.user_id,
    td.SAMPLE,
    td.APPNAME as APP_ID,
    date_trunc(fd.date, month) as month_start,
    min(fd.date) as first_active_date_in_month
  from DWH.DailyUser fd 
  join DWH.Depositors td using (userid)
  group by td.user_id, td.SAMPLE, td.APPNAME, date_trunc(fd.date, month)
),
mtd1 as (
  select 
    'MonthToDate' as TIMELINE,
    fd.date as DATE,
    td.SAMPLE as SAMPLE,
    td.APPNAME as APP_ID,
    sum(fd.revenue) as REVENUE,
    td.user_id as USER_ID,
    -- 标记当前日期是否是该用户当月的首次活跃日
    case when fd.date = ufma.first_active_date_in_month then 1 else 0 end as is_first_active
  from DWH.DailyUser fd 
  join DWH.Depositors td using (userid)
  join user_first_monthly_activity ufma 
    on td.user_id = ufma.user_id
    and td.SAMPLE = ufma.SAMPLE
    and td.APPNAME = ufma.APP_ID
    and date_trunc(fd.date, month) = ufma.month_start
  group by 1,2,3,4,6, ufma.first_active_date_in_month
),
mtd as ( 
  select 
    TIMELINE,
    DATE,
    SAMPLE,
    APP_ID,
    -- 累计收入逻辑保持不变
    sum(revenue) over (
      partition by date_trunc(date, month), sample, app_id 
      order by date 
      range between unbounded preceding and current row
    ) as REVENUE,
    -- 累加首次活跃标记,得到累计唯一用户数
    sum(is_first_active) over (
      partition by date_trunc(date, month), sample, app_id 
      order by date 
      range between unbounded preceding and current row
    ) as ACTIVE_USERS
  from mtd1 
) 
select * from mtd where extract(day from date) = extract(day from current_date) group by 1,2,3,4,5,6

方案二:用ROW_NUMBER标记首次活跃记录

如果不想额外增加CTE,也可以直接在mtd1中用ROW_NUMBER()标记用户当月的第一条记录,再累加计数:

with mtd1 as (
  select 
    'MonthToDate' as TIMELINE,
    fd.date as DATE,
    td.SAMPLE as SAMPLE,
    td.APPNAME as APP_ID,
    sum(fd.revenue) as REVENUE,
    td.user_id as USER_ID,
    -- 对每个用户当月的记录按日期排序,第一条标记为1
    row_number() over (
      partition by td.user_id, date_trunc(fd.date, month), td.SAMPLE, td.APPNAME 
      order by fd.date
    ) as rn
  from DWH.DailyUser fd 
  join DWH.Depositors td using (userid)
  group by 1,2,3,4,6
),
mtd as ( 
  select 
    TIMELINE,
    DATE,
    SAMPLE,
    APP_ID,
    sum(revenue) over (
      partition by date_trunc(date, month), sample, app_id 
      order by date 
      range between unbounded preceding and current row
    ) as REVENUE,
    -- 只累加第一条记录的计数,得到累计唯一用户数
    sum(case when rn = 1 then 1 else 0 end) over (
      partition by date_trunc(date, month), sample, app_id 
      order by date 
      range between unbounded preceding and current row
    ) as ACTIVE_USERS
  from mtd1 
) 
select * from mtd where extract(day from date) = extract(day from current_date) group by 1,2,3,4,5,6

原理说明

这两种方案的核心逻辑一致:每个用户在当月只会被计数一次(在首次活跃的日期),之后通过窗口函数的SUM累加这些首次标记,就能得到从月初到当前日的累计唯一活跃用户数,完美替代了无法直接使用的COUNT(DISTINCT)窗口函数写法。

内容的提问来源于stack exchange,提问作者Porada Kev

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 08:00:00