5

メンバーレベルの変更を含むテーブルがあります。すべてのメンバーのレベルの履歴と変更が発生した日付が含まれています。たとえば、メンバー番号 5 の変更を一覧表示できます。

select * from memberlevelhistory where member = 5

結果:

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

履歴テーブルの最後のエントリは現在のレベルです。

質問: 3 か月以上レベルが 3 以上のすべてのメンバーを一覧表示するにはどうすればよいですか?

それは質問の簡略版です。さらに楽しくするために、この 3 か月間に開始レベルを下回らなかったメンバーのみが必要です。したがって、メンバーがレベル 4 で 3 か月の期間を開始し、最後の月にレベル 3 になった場合、そのメンバーはリストから除外されます。

簡単な質問であっても、どんな助けでも大歓迎です。

拡張版:

また、この期間 >=3 か月のレベル >=3 が過去 6 か月のウィンドウ内で発生する必要があります。

4

2 に答える 2

2

これはnot exists. 次の 3 か月間、現在選択されているレコードより低いレベルのエントリが他にない場合、エントリは有効です。これにより、両方の要件が解決されます。レベルが適切であるが、スパンが不明な場合、メンバーごとの最後のエントリに問題がある可能性があります。私はそれらのレコードを削除することに決めましたが、他のアイデアがあるかもしれません。

例の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
       )

編集:

not exists() を 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

書き直されたクエリ:

; 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 か月間順調に推移しており、これはこの時点の 6 か月前の 3 月に行われたため、含める必要があると思います。

私がしたことは、誰かがうまくいったときに範囲を生成し、この範囲を 3 か月以上の期間と過去 6 か月の終了日でテストすることです。

更新:最終的に正しくなった場合、期間が過去6か月で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

例のSql Fiddleはこちらです。

于 2012-09-03T10:07:15.357 に答える