首先,由于我们正在处理日期,我将构建一个日历表以使事情变得更容易:
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)
其中P
和Q
是关系(表),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` 或之后做某事的所有成员的计数。
请自行测试解决方案;它被包裹在一个未提交的事务中以使其变得容易。