Check if value was greater than x for y consecutive months in change history

I have a table with changes in the level of participants. It has historical changes in the level of all members and the date on which the changes occurred. For example, I can list the changes for member number 5:

select * from memberlevelhistory where member = 5

result:

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

The last entry in the story is the current level.

Question: How to list all participants who had a level higher or equal to 3 for 3 months or more?

This is a simplified version of the question. To make it even more fun, I only need members who have kept up with the entry level during this 3 month period. Therefore, if a member starts a 3-month period from the 4th level and only the 3rd level in the last month, this member is excluded from the list.

Any help, even with a simplified question, is greatly appreciated.

:

>= 3 >= 3 6 .

+5
2

not exists. , , , . . , , . , .

Sql .

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
       )

EDIT:

not exists() "last entry lasting until today".

Sql .

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

:

; 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

Sql Fiddle .

, 100 101 , 3 , , .

, , , - , 3 .

: , , , , . , , - . 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

Sql Fiddle .

+2
SELECT * FROM (
  SELECT MEMBER, CHANGEDATE, LEVEL, COUNT(LEVEL) AS COUNT
  FROM MEMBERLEVELHISTORY
  GROUP BY MEMBER, CHANGEDATE, LEVEL)
WHERE LEVEL >= 3
AND COUNT >= 3;

, , !

0

All Articles