0

我有两张表,一张看起来像这样,用于衡量用户何时加入:

 userId        Date     
 ----      -------------  
   1        01-01-2013       
   2        01-01-2013       
   3        01-01-2013       
   4        01-01-2013       
   5        01-01-2013 ....

另一个看起来像这样的事件与用户相关:

  id    userID    event        Date     
 ----  -----     ---------    ------
   1    1        open         01-01-2013       
   2    2        close        01-02-2013       
   3    2        move         01-03-2013       
   4    3        open         01-04-2013       
   5    5        move         01-05-2013 ....    

我想获得另一个表格,给定一个日期,它会获得在该日期加入的用户中有多少在下一个连续日期或以后的日期发生了事件,并按这些连续日期对它们进行分组。

因此,例如在我的第一个表中,每个用户都加入了相同的日期,所以如果我查询那个日期,我想获得这个:

userCount        Date     
 ----      -------------  
   4        01-01-2013       
   3        01-02-2013       
   3        01-03-2013       
   2        01-04-2013       
   1        01-05-2013 ....

在第一行中,userCount 为 4,因为userId: 1,2,3,5出现在该日期或以后的日期,在下一行中,userCount 为 3,因为userId: 2,3,5 出现在较晚的日期,userId 1并且没有出现在该日期或以后的日期,依此类推。

我如何查询以获得这些结果?

我试图衡量的是用户的无限留存率

4

2 回答 2

0

首先,由于我们正在处理日期,我将构建一个日历表以使事情变得更容易:

create table Calendar
(
    id int primary key identity,
    [date] datetime,
    [day] as datepart(day, [date]) persisted,
    [month] as datepart(month, [date]) persisted,
    [year] as datepart(year, [date]) persisted,

    day_of_year as datepart(dayofyear, [date]) persisted,
    [week] as datepart(week, [date]),

    day_name as datename(dw, [date]),

    is_weekend as 
    case when datepart(dw, [date]) = 7 or datepart(dw, [date]) = 1 
    then 1 else 0 end,

    [quarter] as datepart(quarter, [date]) persisted        
)

go

declare @date datetime
set @date = '1-1-2010'

while @date <= '12-31-2100'
begin

    insert Calendar select @date
    set @date = dateadd(day, 1, @date)

end

因此,您的架构可能看起来像这样:

create table Member
(
    id int primary key identity,
    name nvarchar(100) mot null,
    joined int foreign key references Calendar not null
)

create table MemberActivityType
(
    id int primary key identity,
    name varchar(50) not null
)

insert MemberActivityType 
    select 'Open' union
    select 'Close' union
    select 'Move'

create table MemberActivity
(
    id int primary key identity,
    member_id int foreign key references Member,
    activity_id int foreign key references MemberActivityType,
    [date] int foreign key references Calendar not null
)

现在,当您在查询中需要“if-then”时,Apply运算符可能是您应该考虑的第一个工具。它具有以下形式:

 select * from Q [outer|cross] apply P(q1...qn)

其中PQ是关系(表),q1..qn是 的属性(列),Q表示P(q1..qn)对 P 的查询,涉及q1...qn的某行的值Q。这意味着P(q1..qn)对每个结果进行评估Q

因此,此函数将产生您想要的结果:

create function UnboundedRetention
(
    @join_date int
)
returns @results table(users int, date datetime)
begin       

with Q as
(select K.member_id, K.date anchor from MemberActivity K outer apply

        (select J.member_id, J.date from MemberActivity J 
            where J.date = K.date + 1
            and J.member_id = K.id) L                

        outer apply
                (
                    select A.date from MemberActivity A
                    where A.date > L.date and A.member_id = L.member_id
                ) M

        where K.date >=  @join_date)

    insert @results

        select L.c, C.date from Calendar C inner join Q on Q.anchor = C.id

    cross apply
        (
            select count(M.id) c from Member M where M.id in (select member_id from Q K where K.anchor >= C.id)
        ) L

    return
end

在这种情况下,Q是一个公用表表达式,意思是“对于@join_date成员在其中或之后做某事的每个日期(K),返回该成员做某事的紧随其后的日期,(L)并且对于每个日期L,返回该成员在其中的所有后续日期做了什么(M)

然后,对于 中的每个日期Q,我们返回在该日期 Q` 或之后做某事的所有成员的计数。

请自行测试解决方案;它被包裹在一个未提交的事务中以使其变得容易。

于 2017-08-09T23:31:01.143 回答
0

这就是我的处理方式。设置样本数据:

declare @JoinDate date = '01-01-2013'

declare @users table (UserId int primary key, JoinDate date)

insert into @Users
values
    (1, '01-01-2013'),
    (2, '01-01-2013'),
    (3, '01-01-2013'),
    (4, '01-01-2013'),
    (5, '01-01-2013')

declare @events table 
(
    Id int primary key,
    UserId int,
    EventName varchar(10),
    EventDate date
)

insert into @Events
values
    (1,1,'open','01-01-2013'),
    (2,2,'close','01-02-2013'),
    (3,2,'move','01-03-2013'),
    (4,3,'open','01-04-2013'),
    (5,5,'move','01-05-2013')

第一步是获取在指定日期加入的用户列表(在这种情况下,它恰好是每个人,但我假设这并不总是正确的)。然后将该结果加入到事件中UserId。由于您只关心在加入日期或之后发生的事件,因此您可以将该条件包含在两者之间的加入中

select e.*
from @users u
inner join @Events e
    on u.UserId = e.UserId
        and e.EventDate >= @JoinDate
where u.JoinDate = @JoinDate

EventDate从那里,您可以在两个字段之间的不等式上自行加入该结果集。换句话说,如果某个日期有 4 个用户,则所有 4 个用户都将及时传播到该日期之后的任何行。通过执行 a count(distinct UserId),您可以得出在当天或之后发生事件的不同用户数量。

;with src as
(
    select e.*
    from @users u
    inner join @Events e
        on u.UserId = e.UserId
            and e.EventDate >= @JoinDate
    where u.JoinDate = @JoinDate
)
select 
    UserCount = count(distinct b.UserId),
    EventDate = a.EventDate
from src a
inner join src b
    on a.EventDate <= b.EventDate
group by a.EventDate
order by UserCount desc
于 2017-08-08T19:36:59.643 回答