これは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はこちらです。