Soit une table avec une colonne « date début de période » et une colonne « date fin de période ». On souhaite afficher les lignes dont la période est chevauchée par au moins une autre période.
comment construire la requête ?
La table « tExemple » contient 3 colonnes :
- Id : clef primaire
- DateDebut : type Date
- DateFin : type date
Quatre possibilités de chevauchement de période
Debut-------------------Fin | | | | D1-------F1 D2---F2 D3-------F3 D4--------------------------------F4
- Debut Entre D1 Et F1
- Debut <= D2 Et Fin >= F2
- Fin Entre D3 Et F3
- Debut >= D4 Et Fin <= F4
Points communs entre les chevauchements
Nous remarquons que :
- Debut est toujours <= F1,F2,F3,F4
- Fin est toujours >= D1,D2,D3,D4
Les deux conditions de chevauchement sont donc :
Debut <= Fx Et Fin >= Dx
Notre clause Where de la requête est maintenant construite.
Comment comparer la période de chaque ligne avec les autres ?
Il existe au moins deux solutions avec le SQL d’Access :
– Par une jointure interne (INNER JOIN) de la table avec elle-même avec la condition que les Id soient différents mais cette condition de jointure n’est pas visible dans l’éditeur de requête
– Par une jointure croisée de la table avec elle-même et en ajoutant la condition que les Id soint différents dans la clause Where de la requête. Ce type de jointure est préféré car visible dans l’éditeur.
Une période peut chevaucher plusieurs périodes, il faut donc regrouper les lignes identiques par la clause Group By.
La requête finale (nommée rqChevauchement) est donc :
t.datedebut,
t.datefin
FROM texemple AS t,
texemple AS t2
WHERE t.id <> t2.id
AND
t.datedebut <= t2.datefin
AND
t.datefin >= t2.datedebut
GROUP BY t.id,
t.datedebut,
t.datefin
Pour connaître les lignes qui ne chevauchent aucune autre, une solution consiste à sélectionner les Id qui n’apparaissent pas dans la requête précédente via une jointure externe gauche :
FROM texemple AS t
LEFT JOIN rqchevauchement AS r
ON t.id = r.id
WHERE r.id IS NULL
Performance
Le fait de comparer chaque ligne avec les autres entraîne que la requête est peu performante en terme de vitesse et sera donc exploitable que si la table possède moins de 1000 lignes…
@+
Philippe
Salut Philippe,
J’utilise cette méthode depuis un bon moment dans mon code sql ou autre mais je n’avais jamais penser en faire un article, donc bonne idée ces billets;)
Sinon dans le même genre une autre solution un peu évidente consiste à identifier les cas à exclure (Si Debut>Fx ou Si FinFx Ou FinFx Or Fin
Sinon dans le même genre une autre solution un peu évidente consiste à identifier les cas à exclure :
(Si Debut supérieur à Fx ou Si Fin inférieur à Dx) et à prendre le complément:
Pas ((Debut sup Fx) ou (Fin inf Dx))
Not (Debut sup Fx or Fin inf Dx)
Salut Denis,
Merci pour tes commentaires qui complètent efficacement le billet.
Philippe