【问题标题】:Datewise sum of active users based on status column基于状态列的活跃用户的日期总和
【发布时间】:2021-02-04 16:14:47
【问题描述】:

我有一个包含以下列的 status_log 表:

User_id, isactive, date
1, 1, 1 Jan 2020
2, 1, 1 Jan 2020
3, 1, 2 Jan 2020
2, 0, 5 Jan 2020
4, 1, 10 Jan 2020
4, 0, 10 Jan 2020
3, 0, 12 Jan 2020

Isactive 表示用户从那天起是否处于活动状态,直到 isactive 设置为 false 的那一天。随后,用户可以决定再次变得活跃。以此类推。

我想返回一个包含 2 列的结果。第 1 列应该是从 status_log 表中的第一个日期开始的日期。无论日期是否在 status_log 表中,它都应该有一个日期。对于每一天,我都希望获得当天活跃的用户总数。总和应包括截至当天处于活动状态的所有用户 - 因此那些在该日期或之前将 isactive 设置为 true 并且之前没有将 inactive 设置为 false 的用户。

我正在尝试为此制定算法:

  1. 创建一个日期表,其中包含从表的第一个日期到最后一个日期的所有日期。

  2. 从第 1 步中的表和 status_log 表进行左连接。

  3. 我被困在这里 - 对于每一行,根据所有先前日期的分区执行总和,按日期排序,并且仅包括在当天或前一天设置了 isactive 的那些。如何确保逻辑仅限于活动用户。

预期输出:

Date, activeusers
1 Jan - 2
2 Jan - 3
3 Jan - 3
4 Jan - 3
5 Jan - 2
6 Jan - 2
7 Jan - 2
8 Jan - 2
9 Jan - 2
10 Jan - 2
11 Jan - 2
12 Jan - 1

【问题讨论】:

  • 请展示一些示例数据和所需的输出
  • 对于Create a date table containing all the dates from 1st date of the table till last date,您可以使用永久日历表或recursive cte 即时创建一个
  • 我想知道是否有一种 SQL 方法来获取它而不是日期表。
  • 提到的递归 cte?

标签: sql-server tsql


【解决方案1】:

您可以使用递归公用表表达式来生成日期并在迭代时计算活跃用户的数量。另一种方法是首先生成所有日期并执行计数。

  1. 查找日志开始和结束日期 (cte_period)。
  2. 通过将先前的限制与计数表 (cte_num & cte_dates) 连接起来,生成整个期间的所有日期。此计数表允许最长 27 年的日志周期。如果您需要 273 年,您可以添加一个额外的 cross join
  3. 统计每个日期 (group by cd.date) 的活跃用户数 (count(distinct sl.user_id) ... sl.isactive = 1)。这些活跃用户在当前日期 (not exists ... sl2.isactive = 0) 之前没有停用记录。

样本数据

create table status_log
(
  [user_id] int,
  [isactive] bit,
  [date] date
);

insert into status_log (user_id, isactive, date) values
(1, 1, ' 1 Jan 2020'),
(2, 1, ' 1 Jan 2020'),
(3, 1, ' 2 Jan 2020'),
(2, 0, ' 5 Jan 2020'),
(4, 1, '10 Jan 2020'),
(4, 0, '10 Jan 2020'),
(3, 0, '12 Jan 2020');

解决方案

with cte_period as
(
  select min(sl.date) as date_start,
         max(sl.date) as date_end
  from status_log sl
),
cte_num as
(
  select row_number() over(order by (select null))-1 n
  from       (values (0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) a(n) -- 10 days
  cross join (values (0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) b(n) -- 100 days
  cross join (values (0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) c(n) -- 1000 days (2.7 years)
  cross join (values (0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) d(n) -- 10000 days (27.3 years)
),
cte_dates as
(
  select dateadd(day, cn.n, cp.date_start) as [date]
  from cte_period cp
  cross join cte_num cn
  where dateadd(day, cn.n, cp.date_start) <= cp.date_end
)
select cd.date,
       count(distinct sl.user_id) as activeusers
from cte_dates cd
left join status_log sl
  on  sl.date <= cd.date
  and sl.isactive = 1
  and not exists ( select 'x'
                   from status_log sl2
                   where sl2.user_id = sl.user_id
                     and sl2.date <= cd.date
                     and sl2.date >= sl.date
                     and sl2.isactive = 0 )
group by cd.date
order by cd.date;

结果

date        activeusers
----------  -----------
2020-01-01  2
2020-01-02  3
2020-01-03  3
2020-01-04  3
2020-01-05  2
2020-01-06  2
2020-01-07  2
2020-01-08  2
2020-01-09  2
2020-01-10  2
2020-01-11  2
2020-01-12  1

Fiddle 以查看实际情况和中间结果。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-05-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多