检查更改历史记录中连续y个月的值是否大于x

rum*_*rak 5 sql t-sql

我有成员级别更改的表.它具有所有成员级别的历史变化以及发生变化时的日期.例如,我可以列出会员编号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的时期.

Nik*_*vić 2

这可能是一个案例not exists。如果在接下来的三个月内没有其他条目的级别低于当前所选记录,则该条目有效。这解决了这两个要求。每个成员的最后一个条目可能存在问题,其中级别适当,但跨度未知。我已决定删除这些记录,但您可能有其他想法。

Sql Fiddle 示例在这里

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 并添加了“最后一个条目持续到今天”标准。

Sql Fiddle 示例在这里。

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)

Sql Fiddle 就在这里

我认为 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)

Sql Fiddle 示例在这里