连续日期范围的MySQL实现 [英] MySQL Implementation of Consecutive Date Ranges

查看:471
本文介绍了连续日期范围的MySQL实现的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个包含合同表的MySQL数据库:

I have a MySQL database containing contracts table:

CREATE TABLE IF NOT EXISTS `contracts` (
    `id` BIGINT(20) NOT NULL AUTO_INCREMENT,
    `employee_id` BIGINT(20) DEFAULT NULL,
    `start_date` DATE DEFAULT NULL,
    `end_date` DATE DEFAULT NULL,
    PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

INSERT INTO `contracts` (`id`,`employee_id`,`start_date`,`end_date`) 
VALUES 
(1, 555, '2010-01-01', '2012-12-31'), 
(2, 666, '2013-01-01', '2013-05-01'), 
(3, 666, '2013-05-02', '2013-10-11'),
(4, 777, '2012-01-10', '2013-03-01'),
(5, 777, '2013-03-02', '2014-07-15'),
(6, 777, '2015-01-16', '2015-05-20');

查询它,每位员工获得一个或多个合同行

Querying it I get one or multiple contract lines per employee

SELECT * FROM contracts


id  employee_id     start_date  end_date
1   555             2010-01-01  2012-12-31
2   666             2013-01-01  2013-05-01
3   666             2013-05-02  2013-10-11
4   777             2012-01-10  2013-03-01
5   777             2013-03-02  2014-07-15
6   777             2015-01-16  2015-05-20

如何查询合同表以将每个员工的连续范围分组?我正在寻找此输出:

How do I query contracts table to group consecutive ranges per employee? I'm looking for this output:

employee_id     start_date  end_date
555             2010-01-01  2012-12-31
666             2013-01-01  2013-10-11
777             2012-01-10  2014-07-15
777             2015-01-16  2015-05-20

考虑到合同日期之间没有间隔,员工666的记录将返回最低的开始日期和最高的结束日期.

Record for employee 666 would return lowest start date and highest end date taking into account there is no gap between contract dates.

员工777的记录将返回两行,因为记录ID 5和6之间有间隔.

Record for employee 777 would return two lines since there is a gap between record id 5 and 6.

有什么想法吗?

推荐答案

逻辑并不难,但是在MySQL中的实现却很困难.这个想法是添加一个标志来指示合同开始的开始.然后,对于每一行,对其进行累加.累积总和可用于分组.

The logic isn't so hard, but the implementation in MySQL is. The idea is to add a flag that indicates the beginning of a contract start. Then, for each row, do a cumulative sum of this. The cumulative sum can be used for grouping purposes.

第一步可以使用相关子查询:

The first step can use a correlated subquery:

   SELECT c1.*, 
       (NOT EXISTS (SELECT 1
                    FROM contracts c2
                    WHERE c1.employee_id = c2.employee_id AND
                          c1.start_date = c2.end_date + INTERVAL 1 DAY
                   )
       ) AS startflag
FROM contracts c1;

第二个将其用作子查询并进行累加:

The second uses this as a subquery and does a cumulative sum:

    SELECT 
c0.*
,(@rn := @rn + COALESCE(startflag, 0)) AS cumestarts
FROM 
(SELECT c1.*,
             (NOT EXISTS (SELECT 1
                          FROM contracts c2
                          WHERE c1.employee_id = c2.employee_id AND
                                c1.start_date = c2.end_date + INTERVAL 1 DAY
                         )
             ) AS startflag
      FROM contracts c1
      ORDER BY employee_id, start_date

) c0 CROSS JOIN (SELECT @rn := 0) params;

最后一步是按以下值进行汇总:

The final step is to aggregate by this value:

SELECT 
c.employee_id
,MIN(c.start_date) AS start_date
,MAX(c.end_date) AS end_date
,COUNT(*) AS numcontracts
FROM 
(
        SELECT 
        c0.*
        ,(@rn := @rn + COALESCE(startflag, 0)) AS cumestarts
        FROM 
        (SELECT c1.*,
                 (NOT EXISTS (SELECT 1
                      FROM contracts c2
                      WHERE c1.employee_id = c2.employee_id AND
                        c1.start_date = c2.end_date + INTERVAL 1 DAY
                     )
                 ) AS startflag
              FROM contracts c1
              ORDER BY employee_id, start_date

        ) c0 CROSS JOIN (SELECT @rn := 0) params

) c
GROUP BY c.employee_id, c.cumestarts

这篇关于连续日期范围的MySQL实现的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆