我有成员级别更改的表.它具有所有成员级别的历史变化以及发生变化时的日期.例如,我可以列出会员编号5的更改:
select * from memberlevelhistory where member = 5
Run Code Online (Sandbox Code Playgroud)
结果:
member changedate level
5 2012-04-01 2
5 2012-03-01 3
5 2012-02-01 2
5 2011-02-01 6
5 2011-02-01 6
5 2010-03-15 6
5 2010-02-01 5
5 2010-01-01 5
5 2009-10-01 4
5 2009-08-27 2
5 2009-08-01 1
Run Code Online (Sandbox Code Playgroud)
历史表中的最后一个条目是当前级别.
问题:如何列出3个月或更长时间内水平高于或等于3的所有成员?
这是问题的简化版本.为了让它变得更有趣,我只需要在这3个月期间内没有低于起始水平的成员.因此,如果一个成员在4个月期间开始了4级并且在上个月只是3级,那么该成员将被排除在列表之外.
任何帮助,即使是简化的问题,我们都非常感谢.
扩大的视野:
我还需要在最近6个月的窗口内发生> = 3个月的水平> = 3的时期.
这可能是一个案例not exists。如果在接下来的三个月内没有其他条目的级别低于当前所选记录,则该条目有效。这解决了这两个要求。每个成员的最后一个条目可能存在问题,其中级别适当,但跨度未知。我已决定删除这些记录,但您可能有其他想法。
select distinct mlh.member
from memberlevelhistory mlh
where mlh.level >= 3
and not exists
(
select null
from memberlevelhistory mlh2
where mlh2.member = mlh.member
and mlh2.changedate >= mlh.changedate
and mlh2.changedate < dateadd(month, 3, mlh.changedate)
and mlh2.level < mlh.level
)
-- The last entry might have appropriate level
-- But we cannot tell how long it lasted,
-- So we are going to remove it.
and exists
(
select null
from memberlevelhistory mlh3
where mlh3.member = mlh.member
and mlh3.changedate > mlh.changedate
)
Run Code Online (Sandbox Code Playgroud)
编辑:
我已将 not contains() 重写为 left join 并添加了“最后一个条目持续到今天”标准。
select distinct mlh.member
from memberlevelhistory mlh
left join memberlevelhistory mlh2
on mlh2.member = mlh.member
and mlh2.changedate >= mlh.changedate
and mlh2.changedate < dateadd(month, 3, mlh.changedate)
and mlh2.level < mlh.level
where mlh.level >= 3
and mlh2.member is null
and datediff(month, mlh.changedate, getdate()) >= 3
Run Code Online (Sandbox Code Playgroud)
查询重写:
; with ranges as
(
select mlh.member, mlh.changedate StartRange, min(isnull(mlh2.changedate, getdate())) EndDate
from memberlevelhistory mlh
left join memberlevelhistory mlh2
on mlh2.member = mlh.member
and mlh2.changedate >= mlh.changedate
and mlh2.level < mlh.level
where mlh.level >= 3
group by mlh.member, mlh.changedate
having datediff (month, min(isnull(mlh2.changedate, getdate())), getdate()) <= 6
and datediff (month, mlh.changedate, min(isnull(mlh2.changedate, getdate()))) >= 3
)
select distinct member
from ranges
Run Code Online (Sandbox Code Playgroud)
我认为 100 和 101 应该包括在内,因为两者都在 3 个月内表现良好,而且这是在 3 月份进行的,也就是在此之前的六个月。
我所做的就是在某人跑得很好时生成范围,然后测试该范围的持续时间为 3 个月或更长,结束日期为过去 6 个月。
更新:如果我最终做对了,您需要该持续时间在过去六个月中持续三个月。计算一下,可以将更改截断为当前日期 - 六个月。使用它作为起点,并找到范围的终点作为第一个mlh具有较低级别和较高日期作为终点的终点,有足够的信息来计算持续时间。
; with ranges as
(
select mlh.member,
-- If good range starts more than six months before today
-- truncate it to today - 6 months
case when datediff (month, mlh.changedate, getdate()) > 6
then dateadd(month, -6, getdate())
else mlh.changedate
end StartRange,
-- First bad mlh after current changedate
min(isnull(mlh2.changedate, getdate())) EndRange
from memberlevelhistory mlh
left join memberlevelhistory mlh2
on mlh2.member = mlh.member
and mlh2.changedate >= mlh.changedate
and mlh2.level < mlh.level
where mlh.level >= 3
group by mlh.member, mlh.changedate
-- As above, limit good range to max six months before today
-- And only get those lasting at least three months
having datediff (month, case when datediff(month, mlh.changedate, getdate()) > 6
then dateadd(month, -6, getdate())
else mlh.changedate
end,
min(isnull(mlh2.changedate, getdate()))) >= 3
)
select distinct member
from ranges
Run Code Online (Sandbox Code Playgroud)