如何识别某人是否在SQL中的特定日期注册

时间:2015-09-25 01:30:46

标签: mysql

我有下表:

+----------+------------+------------+
| 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。

业务规则:

  • 即使会员在会员资格方面有一天的差距,他也会被取消资格。
  • 相同的成员ID可能在表中有不同的条目(示例MemberID 10)
  • 在演示表中,成员10,20,50应包含在结果表中,因为它们在整个期间都有资格

3 个答案:

答案 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标志列。

  1. 首先将所有这些(符合条件)设置为true
  2. 然后用这样的
  3. 将所选的那些设置为false

     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且下一行的StartDate2015-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

您可以考虑迭代MemberIDStartDateif(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'

Reuslt

+----------+------------+------------+
| MemberID | StartDate  | EndDate    |
+----------+------------+------------+
|       10 | 2015-01-01 | 9999-12-31 |
|       20 | 2015-01-01 | 9999-12-31 |
|       50 | 2015-01-01 | 2015-08-31 |
+----------+------------+------------+