2015-07-14 1 views
0

J'ai une base MySQL contenant table contrats:MySQL Mise en œuvre Date Consécutive Ranges

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

Interrogation j'obtenir une ou plusieurs lignes de contrat par employé

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 

Comment interroger table contrats groupe de plages consécutives par employé? Je cherche cette sortie:

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 

record pour l'employé 666 retournerait date de début le plus bas et date de fin le plus élevé en tenant compte il n'y a pas d'écart entre les dates contractuelles.

record pour l'employé 777 retournerait deux lignes car il y a un décalage entre 5 et ID d'enregistrement 6.

Toutes les idées?

Répondre

1

La logique n'est pas si difficile, mais l'implémentation dans MySQL est. L'idée est d'ajouter un drapeau qui indique le début d'un début de contrat. Ensuite, pour chaque ligne, faites une somme cumulée de ceci. La somme cumulative peut être utilisée à des fins de regroupement.

La première étape peut utiliser une sous-requête corrélée:

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; 

La seconde utilise comme une sous-requête et effectue une somme cumulée:

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; 

La dernière étape consiste à agréger par cette valeur:

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

C'est parfait. Merci. Vous avez corrigé votre réponse pour que sql fonctionne sans erreur. –