MySQL实现连续日期范围

时间:2015-07-14 14:43:06

标签: mysql date date-range

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

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');

查询我每个员工获得一个或多个合同行

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

如何查询合约表以对每位员工的连续范围进行分组?我正在寻找这个输出:

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的记录将返回最低开始日期和最高结束日期,同时考虑到合同日期之间没有差距。

员工777的记录将返回两行,因为记录ID 5和6之间存在差距。

有什么想法吗?

1 个答案:

答案 0 :(得分:1)

逻辑并不那么难,但MySQL中的实现是。想法是添加一个标志,指示合同开始的开始。然后,对于每一行,执行此累计和。累积金额可用于分组目的。

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

   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;

第二个使用它作为子查询并进行累计求和:

    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;

最后一步是按此值汇总:

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