MySQL-如何进行自连接以返回重叠的日期范围?

MySQL-如何进行自连接以返回重叠的日期范围?,mysql,self-join,overlapping-matches,Mysql,Self Join,Overlapping Matches,我有一份由组织内成员持有的历史职位任期表。 每个职位一次只能由一个人担任,但一个人可以连续或同时担任多个职位。 我想通过依次获取每个职位任期记录,然后将其与表中的每个其他记录进行比较来检查此表的完整性,以查找与同一职位的其他任期的严重重叠 我在下面编写了一个工作查询,它返回通过测试时重叠的详细信息\u任期\u id、职位\u id以及特定任期的开始和结束日期,请参见下文 对于每个重叠部分。此查询返回任期id、成员id、成员编号、开始日期、结束日期以及重叠原因 有谁能帮我用sql对同一个tbl_t

我有一份由组织内成员持有的历史职位任期表。 每个职位一次只能由一个人担任,但一个人可以连续或同时担任多个职位。 我想通过依次获取每个职位任期记录,然后将其与表中的每个其他记录进行比较来检查此表的完整性,以查找与同一职位的其他任期的严重重叠

我在下面编写了一个工作查询,它返回通过测试时重叠的详细信息\u任期\u id、职位\u id以及特定任期的开始和结束日期,请参见下文 对于每个重叠部分。此查询返回任期id、成员id、成员编号、开始日期、结束日期以及重叠原因

有谁能帮我用sql对同一个tbl_tenue表运行这个查询,每次都将tbl_tenue中一行的数据传递给它,这样我就可以测试每个记录是否与除其自身之外的其他记录重叠,并从记录及其所有重叠处返回数据

我意识到,如果我能做到这一点,那么我应该能够避免使用连接将任期id传递给WHERE子句,也可以避免使用连接传递日期,但目前我不知道如何做到这一点,因此任何帮助都是好的

下面的查询使用了以下表格,这些表格对此问题进行了简化

TABLE tbl_member 
  ( member_id INT AUTO_INCREMENT, -- pk
    member_sn` varchar(50) , --surname
    <other stuff>
  )

TABLE tbl_tenure
  (tenure_id INT AUTO_INCREMENT, -- pk
   member_id INT -- fk to tbl_member
   position_id -- fk to table of position titles
   date_started DATE
   date_ended DATE -- will be NULL if still in post
  )


  -- test data for query
  SET @the_test_tenure_start_date = '2016-05-13' ;
  SET @the_test_tenure_end_date = '2016-10-05';
  SET @the_test_position_id = 18; 
  SET @the_test_tenue_id = 122;

-- the query to return overlaps with data from a given tenure record  
SELECT
   tbl_tenure.tenure_id,
   tbl_tenure.member_id,
   tbl_member.member_sn,
   tbl_tenure.date_started,
   tbl_tenure.date_ended,
   CASE
      WHEN @the_test_tenure_end_date <= IFNULL(date_ended, CURDATE())  -- test end date <= existing end date
      AND @the_test_tenure_start_date >= date_started   -- test start date >= existing start date
      THEN 'Test dates fall completely inside an existing tenure'

      WHEN @the_test_tenure_end_date >= IFNULL(date_ended, CURDATE())   -- test end date >= existing end date
      AND @the_test_tenure_start_date <= date_started   -- test start date <= existing start date
      THEN 'An existing tenure falls completely inside test dates'

      WHEN @the_test_tenure_start_date >= date_started   -- test start date >= existing start date
      AND @the_test_tenure_start_date <= IFNULL(date_ended, CURDATE())   -- test start date <= existing end date
      THEN 'Test start date overlaps with an existing tenure'

      WHEN @the_test_tenure_end_date >= date_started   -- test end date >= existing start date
      AND @the_test_tenure_end_date <= IFNULL(date_ended, CURDATE())   -- test end date <= existing end date
      THEN 'Test end date overlaps with an existing tenure'
   END AS reason

FROM
   tbl_tenure
   INNER JOIN tbl_member
      ON tbl_tenure.member_id = tbl_member.member_id

WHERE ( -- there is an overlap (see qry 2.2 http://salman-w.blogspot.co.uk/2012/06/sql-query-overlapping-date-ranges.html
          @the_test_tenure_end_date >= date_started)    
          AND 
      IFNULL(date_ended, CURDATE()) >= @the_test_tenure_start_date
      )   
   AND tbl_tenure.position_id = @the_test_position_id   -- position to be tested
   AND  tbl_tenure.tenure_id <> @the_test_tenue_id    -- don't look at the test tenure record
ORDER BY tbl_tenure.date_started ASC;

花了一天的时间在这上面,在跑步机上跑步时思考了一下,我相信我找到了答案。我把它贴在这里是为了其他人的利益。我将join with tbl_成员移动到一个子查询中,为了完整起见,包括另一个子查询,以从第三个表tbl_position获取职位的实际标题,如下所示。看不到用联接替换子查询的方法,但这无关紧要

下面是我编写的代码,它似乎工作正常,并显示了所有重叠,详细说明了谁与谁重叠、何时重叠以及为什么重叠

比如说,如果弗雷德的总统职位与吉姆的现有记录重叠,原因是弗雷德的任期与吉姆的任期完全一致,那么吉姆的总统职位也与弗雷德的现有任期重叠,原因是弗雷德的任期与吉姆的任期完全一致。我得到重叠的两边

如果有一个快速的方法可以得到一个“单向”重叠,那么一定要给出一个更好的答案

我的回答

SELECT
   base_tenure.position_id     AS base_tenure_id,
   base_tenure.member_id     AS base_member_id,
   (SELECT member_sn FROM tbl_member WHERE tbl_member.member_id = base_tenure.member_id)     AS base_sn,
   (SELECT tbl_positions.position FROM tbl_positions WHERE tbl_positions.position_id = base_tenure.position_id ) AS POSITION,
   base_tenure.date_started  AS base_date_started,
   base_tenure.date_ended    AS base_date_ended,

   overlap_tenure.position_id  AS overlap_tenure_id,
   overlap_tenure.member_id AS overlap_member_id,
   (SELECT member_sn FROM tbl_member WHERE tbl_member.member_id = overlap_tenure.member_id)     AS overlap_sn,
   overlap_tenure.date_started AS overlap_date_started,
   overlap_tenure.date_ended   AS overlap_date_ended,  

   CASE
      WHEN base_tenure.date_ended <= IFNULL(overlap_tenure.date_ended, CURDATE())-- test end date <= existing end date
      AND base_tenure.date_started >= overlap_tenure.date_started  -- test start date >= existing start date
      THEN 'tbl_member dates fall completely inside an existing tenue'

      WHEN base_tenure.date_ended >= IFNULL(overlap_tenure.date_ended, CURDATE()) -- test end date >= existing end date
      AND base_tenure.date_started <=  overlap_tenure.date_started -- test start date <= existing start date
      THEN 'An existing tenue falls completely inside tbl_member dates'

      WHEN base_tenure.date_started >= overlap_tenure.date_started  -- test start date >= existing start date
      AND base_tenure.date_started <= IFNULL( overlap_tenure.date_ended , CURDATE()) -- test start date <= existing end date
      THEN 'tbl_member start date overlaps with an existing tenue'

      WHEN base_tenure.date_ended >= overlap_tenure.date_started  -- test end date >= existing start date
      AND base_tenure.date_ended <= IFNULL( overlap_tenure.date_ended , CURDATE())-- test end date <= existing end date
      THEN 'tbl_member end date overlaps with an existing tenue'
   END AS reason

FROM -- a self join on tbl_tenure
   tbl_tenure AS base_tenure,
   tbl_tenure AS overlap_tenure

WHERE (-- there is an overlap (see qry 2.2 http://salman-w.blogspot.co.uk/2012/06/sql-query-overlapping-date-ranges.html
           base_tenure.date_ended  >= overlap_tenure.date_started -- test end date >= existing start date
          AND 
          IFNULL(overlap_tenure.date_ended, CURDATE()) >= base_tenure.date_started 
          )

   AND 
      base_tenure.club_function_id = overlap_tenure.club_function_id -- positions are the same for both members

   AND  
      base_tenure.position_id <> overlap_tenure.position_id --  don't compare the base record with itself as they are identical and will always overlap 

 ORDER BY
       (SELECT member_sn FROM tbl_member WHERE tbl_member.member_id = base_tenure.member_id) ,
       base_tenure.date_started ;  

花了一天的时间在这上面,在跑步机上跑步时思考了一下,我相信我找到了答案。我把它贴在这里是为了其他人的利益。我将join with tbl_成员移动到一个子查询中,为了完整起见,包括另一个子查询,以从第三个表tbl_position获取职位的实际标题,如下所示。看不到用联接替换子查询的方法,但这无关紧要

下面是我编写的代码,它似乎工作正常,并显示了所有重叠,详细说明了谁与谁重叠、何时重叠以及为什么重叠

比如说,如果弗雷德的总统职位与吉姆的现有记录重叠,原因是弗雷德的任期与吉姆的任期完全一致,那么吉姆的总统职位也与弗雷德的现有任期重叠,原因是弗雷德的任期与吉姆的任期完全一致。我得到重叠的两边

如果有一个快速的方法可以得到一个“单向”重叠,那么一定要给出一个更好的答案

我的回答

SELECT
   base_tenure.position_id     AS base_tenure_id,
   base_tenure.member_id     AS base_member_id,
   (SELECT member_sn FROM tbl_member WHERE tbl_member.member_id = base_tenure.member_id)     AS base_sn,
   (SELECT tbl_positions.position FROM tbl_positions WHERE tbl_positions.position_id = base_tenure.position_id ) AS POSITION,
   base_tenure.date_started  AS base_date_started,
   base_tenure.date_ended    AS base_date_ended,

   overlap_tenure.position_id  AS overlap_tenure_id,
   overlap_tenure.member_id AS overlap_member_id,
   (SELECT member_sn FROM tbl_member WHERE tbl_member.member_id = overlap_tenure.member_id)     AS overlap_sn,
   overlap_tenure.date_started AS overlap_date_started,
   overlap_tenure.date_ended   AS overlap_date_ended,  

   CASE
      WHEN base_tenure.date_ended <= IFNULL(overlap_tenure.date_ended, CURDATE())-- test end date <= existing end date
      AND base_tenure.date_started >= overlap_tenure.date_started  -- test start date >= existing start date
      THEN 'tbl_member dates fall completely inside an existing tenue'

      WHEN base_tenure.date_ended >= IFNULL(overlap_tenure.date_ended, CURDATE()) -- test end date >= existing end date
      AND base_tenure.date_started <=  overlap_tenure.date_started -- test start date <= existing start date
      THEN 'An existing tenue falls completely inside tbl_member dates'

      WHEN base_tenure.date_started >= overlap_tenure.date_started  -- test start date >= existing start date
      AND base_tenure.date_started <= IFNULL( overlap_tenure.date_ended , CURDATE()) -- test start date <= existing end date
      THEN 'tbl_member start date overlaps with an existing tenue'

      WHEN base_tenure.date_ended >= overlap_tenure.date_started  -- test end date >= existing start date
      AND base_tenure.date_ended <= IFNULL( overlap_tenure.date_ended , CURDATE())-- test end date <= existing end date
      THEN 'tbl_member end date overlaps with an existing tenue'
   END AS reason

FROM -- a self join on tbl_tenure
   tbl_tenure AS base_tenure,
   tbl_tenure AS overlap_tenure

WHERE (-- there is an overlap (see qry 2.2 http://salman-w.blogspot.co.uk/2012/06/sql-query-overlapping-date-ranges.html
           base_tenure.date_ended  >= overlap_tenure.date_started -- test end date >= existing start date
          AND 
          IFNULL(overlap_tenure.date_ended, CURDATE()) >= base_tenure.date_started 
          )

   AND 
      base_tenure.club_function_id = overlap_tenure.club_function_id -- positions are the same for both members

   AND  
      base_tenure.position_id <> overlap_tenure.position_id --  don't compare the base record with itself as they are identical and will always overlap 

 ORDER BY
       (SELECT member_sn FROM tbl_member WHERE tbl_member.member_id = base_tenure.member_id) ,
       base_tenure.date_started ;  

看见请注意,如果事件B在事件A结束之前开始,而在事件A开始之后结束,则可以说事件B与事件A重叠。但这并不区分B完全封闭在A内或B延伸到A之外的情况。我的案例陈述中的案例1和案例3就是这样做的,并提供了不同的原因。此外,我的问题是关于在自连接中使用此查询,而不是检测重叠。至于MCVE,我不确定我是否理解您的观点。我想我给出了一个关于表模式的清晰问题和一个其他人可以从中受益的示例查询。是的。我同意,这是我的观点。如果A从2到8。然后,如果B从4-6、4-10、1-10或6-10开始,它们都将被您提出的条件检测到。我对此没有异议。然而,这种情况并不区分,例如B重叠,因为B4-6完全封闭在A2-8内,而当B重叠时,因为B4-10的起点在A的范围内,而不是终点。我需要区分所有四种可能的重叠情况,例如B1-4、B3-10、B1-10和B4-6,以便给出正确的原因。但是,正如我所说,这是我想要帮助的自我加入。顺便问一下,否决我收到的赞成票的人能否解释他们为什么这样做,以便我今后避免犯同样的错误
看见请注意,如果事件B在事件A结束之前开始,而在事件A开始之后结束,则可以说事件B与事件A重叠。但这并不区分B完全封闭在A内或B延伸到A之外的情况。我的案例陈述中的案例1和案例3就是这样做的,并提供了不同的原因。此外,我的问题是关于在自连接中使用此查询,而不是检测重叠。至于MCVE,我不确定我是否理解您的观点。我想我给出了一个关于表模式的清晰问题和一个其他人可以从中受益的示例查询。是的。我同意,这是我的观点。如果A从2到8。然后,如果B从4-6、4-10、1-10或6-10开始,它们都将被您提出的条件检测到。我对此没有异议。然而,这种情况并不区分,例如B重叠,因为B4-6完全封闭在A2-8内,而当B重叠时,因为B4-10的起点在A的范围内,而不是终点。我需要区分所有四种可能的重叠情况,例如B1-4、B3-10、B1-10和B4-6,以便给出正确的原因。但是,正如我所说,这是我想要帮助的自我加入。顺便问一下,否决我收到的赞成票的人能否解释他们为什么这样做,以便我今后避免犯同样的错误
SELECT
   base_tenure.position_id     AS base_tenure_id,
   base_tenure.member_id     AS base_member_id,
   (SELECT member_sn FROM tbl_member WHERE tbl_member.member_id = base_tenure.member_id)     AS base_sn,
   (SELECT tbl_positions.position FROM tbl_positions WHERE tbl_positions.position_id = base_tenure.position_id ) AS POSITION,
   base_tenure.date_started  AS base_date_started,
   base_tenure.date_ended    AS base_date_ended,

   overlap_tenure.position_id  AS overlap_tenure_id,
   overlap_tenure.member_id AS overlap_member_id,
   (SELECT member_sn FROM tbl_member WHERE tbl_member.member_id = overlap_tenure.member_id)     AS overlap_sn,
   overlap_tenure.date_started AS overlap_date_started,
   overlap_tenure.date_ended   AS overlap_date_ended,  

   CASE
      WHEN base_tenure.date_ended <= IFNULL(overlap_tenure.date_ended, CURDATE())-- test end date <= existing end date
      AND base_tenure.date_started >= overlap_tenure.date_started  -- test start date >= existing start date
      THEN 'tbl_member dates fall completely inside an existing tenue'

      WHEN base_tenure.date_ended >= IFNULL(overlap_tenure.date_ended, CURDATE()) -- test end date >= existing end date
      AND base_tenure.date_started <=  overlap_tenure.date_started -- test start date <= existing start date
      THEN 'An existing tenue falls completely inside tbl_member dates'

      WHEN base_tenure.date_started >= overlap_tenure.date_started  -- test start date >= existing start date
      AND base_tenure.date_started <= IFNULL( overlap_tenure.date_ended , CURDATE()) -- test start date <= existing end date
      THEN 'tbl_member start date overlaps with an existing tenue'

      WHEN base_tenure.date_ended >= overlap_tenure.date_started  -- test end date >= existing start date
      AND base_tenure.date_ended <= IFNULL( overlap_tenure.date_ended , CURDATE())-- test end date <= existing end date
      THEN 'tbl_member end date overlaps with an existing tenue'
   END AS reason

FROM -- a self join on tbl_tenure
   tbl_tenure AS base_tenure,
   tbl_tenure AS overlap_tenure

WHERE (-- there is an overlap (see qry 2.2 http://salman-w.blogspot.co.uk/2012/06/sql-query-overlapping-date-ranges.html
           base_tenure.date_ended  >= overlap_tenure.date_started -- test end date >= existing start date
          AND 
          IFNULL(overlap_tenure.date_ended, CURDATE()) >= base_tenure.date_started 
          )

   AND 
      base_tenure.club_function_id = overlap_tenure.club_function_id -- positions are the same for both members

   AND  
      base_tenure.position_id <> overlap_tenure.position_id --  don't compare the base record with itself as they are identical and will always overlap 

 ORDER BY
       (SELECT member_sn FROM tbl_member WHERE tbl_member.member_id = base_tenure.member_id) ,
       base_tenure.date_started ;