我有下表:
+----------+------------+------------+
| MemberID | StartDate | EndDate |
+----------+------------+------------+
| 10 | 2015-01-01 | 9999-12-31 |
| 10 | 2015-06-15 | 9999-12-31 |
| 20 | 2015-01-01 | 2015-04-06 |
| 20 | 2015-04-07 | 9999-12-31 |
| 30 | 2015-05-06 | 9999-12-31 |
| 40 | 2015-01-01 | 2015-03-01 |
| 50 | 2015-01-01 | 2015-08-31 |
+----------+------------+------------+
根据此表,我想找到2015年1月1日至2015年8月31日期间连续资格的会员ID。
业务规则:
答案 0 :(得分:0)
这是一个间隙和岛屿问题,在MySQL中相当痛苦。我们的想法是为每一行分配一个组ID。组ID是期间开始的累积总和。并且,在没有先前记录的情况下开始一段时间。
因此,以下应该分配该组,假设StartDate
总是在前一个EndDate
之后的一天(这个条件可以放宽,但它稍微简化了编码):
select t.*,
(@grp := if(@m = MemberId and @e = date_sub(StartDate, interval 1 day), @grp,
if(@m := MemberId, if(@e := EndDate, @grp + 1, @grp + 1), @grp + 1
)
)
) as grp
from (select t.*,
(select 1
from table t2
where t2.MemberId = t.MemberId and
t2.EndDate = date_sub(t.StartDate, interval 1 day)
) as IsPeriodStart
from table t
) t cross join
(select @m := 0, @e := 0, @grp := 0) params
order by MemberId, StartDate;
然后,您的问题的答案只是一个带有having
子句的聚合:
select MemberId
from (select t.*,
(@grp := if(@m = MemberId and @e = date_sub(StartDate, interval 1 day), @grp,
if(@m := MemberId, if(@e := EndDate, @grp + 1, @grp + 1), @grp + 1
)
)
) as grp
from (select t.*,
(select 1
from table t2
where t2.MemberId = t.MemberId and
t2.EndDate = date_sub(t.StartDate, interval 1 day)
) as IsPeriodStart
from table t
) t cross join
(select @m := 0, @e := 0, @grp := 0) params
order by MemberId, StartDate
) t
group by MemberId, grp
having min(StartDate) <= '2015-01-01' and max(EndDate) >- '2015-08-31';
编辑:
我意识到这可以大大简化。你不需要变量。如果在此期间IsPeriodStart
标志永远不为真,那么在此期间某人是连续的:
select MemberId
from (select t.*,
(select 1
from table t2
where t2.MemberId = t.MemberId and
t2.EndDate = date_sub(t.StartDate, interval 1 day)
) as IsPeriodStart
from table t
where EndDate >= '2015-01-01' and StartDate <= '2015-08-31'
) t
group by MemberId
having max(case when IsPeriodStart then StartDate end) = min(StartDate) and
min(StartDate) <= '2015-01-01' and
max(EndDate) >= '2015-08-31';
having
子句中的三个条件可以完成工作。最后两个应该是显而易见的 - 期限是涵盖的。首先是说该范围内唯一的期间是第一个记录;因此,没有差距。
答案 1 :(得分:0)
另一种方法是,如果您有一个成员表,请添加Eligible
标志列。
update Member set Eligible = false
where Member.MemberId =
(select A.MemberId from tbl A
left outer join tbl B on A.MemberId = B.MemberId
where A.EndDate <> '9999-12-31'
and A.EndDate +1 < B.StartDate)
注意它不严格的mysql,因为我不知道你的字段的数据类型。
答案 2 :(得分:0)
这里的方法是:
1。找到成员的连续日期,即如果EndDate为2015-04-06
且下一行的StartDate
为2015-04-07
,请将它们合并为单行。这是通过以下声明实现的:
SELECT
MemberID, MIN(StartDate) StartDate, MAX(EndDate) EndDate
FROM
(SELECT
dt.*,
IF(@previd = dt.memberid
&& DATE_ADD(@prevdate, INTERVAL 1 DAY) <= dt.StartDate, @groupid, @groupid:=@groupid + 1) GroupId,
@previd:=dt.memberid,
@prevdate:=dt.EndDate
FROM
data_table dt, (SELECT @previd:=NULL, @prevdate:=NULL, @groupid:=0) a
ORDER BY MemberID , StartDate) memberdategrouptable
GROUP BY GroupId
您可以考虑迭代MemberID
,StartDate
和if(found_different_member || (member_same_as_previous_row && previous_end_date_does_not_match_to_current_start_date) assign_new_group_id
排序的结果。
这将获取以下结果:
+----------+------------+------------+---------+----------------------+-----------------------+
| MemberID | StartDate | EndDate | GroupId | @previd:=dt.memberid | @prevdate:=dt.EndDate |
+----------+------------+------------+---------+----------------------+-----------------------+
| 10 | 2015-01-01 | 9999-12-31 | 1 | 10 | 9999-12-31 |
| 10 | 2015-06-15 | 9999-12-31 | 2 | 10 | 9999-12-31 |
| 20 | 2015-01-01 | 2015-04-06 | 3 | 20 | 2015-04-06 |
| 20 | 2015-04-07 | 9999-12-31 | 3 | 20 | 9999-12-31 |
| 30 | 2015-05-06 | 9999-12-31 | 4 | 30 | 9999-12-31 |
| 40 | 2015-01-01 | 2015-03-01 | 5 | 40 | 2015-03-01 |
| 50 | 2015-01-01 | 2015-08-31 | 6 | 50 | 2015-08-31 |
+----------+------------+------------+---------+----------------------+-----------------------+
+----------+------------+------------+
| MemberID | StartDate | EndDate |
+----------+------------+------------+
| 10 | 2015-01-01 | 9999-12-31 |
| 10 | 2015-06-15 | 9999-12-31 |
| 20 | 2015-01-01 | 9999-12-31 |
| 30 | 2015-05-06 | 9999-12-31 |
| 40 | 2015-01-01 | 2015-03-01 |
| 50 | 2015-01-01 | 2015-08-31 |
+----------+------------+------------+
2. 列出符合日期条件的商品
SELECT
*
FROM
(SELECT
MemberID, MIN(StartDate) StartDate, MAX(EndDate) EndDate
FROM
(SELECT
dt.*,
IF(@previd = dt.memberid
&& DATE_ADD(@prevdate, INTERVAL 1 DAY) <= dt.StartDate, @groupid, @groupid:=@groupid + 1) GroupId,
@previd:=dt.memberid,
@prevdate:=dt.EndDate
FROM
data_table dt, (SELECT @previd:=NULL, @prevdate:=NULL, @groupid:=0) a
ORDER BY MemberID , StartDate) memberdategrouptable
GROUP BY GroupId) memberdaterange
WHERE
StartDate <= '2015-01-01'
AND EndDate >= '2015-08-31'
+----------+------------+------------+
| MemberID | StartDate | EndDate |
+----------+------------+------------+
| 10 | 2015-01-01 | 9999-12-31 |
| 20 | 2015-01-01 | 9999-12-31 |
| 50 | 2015-01-01 | 2015-08-31 |
+----------+------------+------------+