Bonjour,

Dans le cadre de mon travail, on m'a demandé de calculer le nombre de jours non ouvrés entre deux dates, en tenant compte à la fois des week-ends, mais aussi des jours fériés. Les recherches que j'ai faites sur internet ne m'ont fournies que des solutions en PL/SQL qui ne me convenaient pas. Je viens donc ici pour vous proposer la solution que j'ai écrite en SQL.

Au cas où qqn serait confronté au même problème, j'espère pour lui qu'il tombera sur cette solution

Le résultat est un nombre décimal car une date de début ou de fin peut très bien tomber au milieu d'un week-end ou d'un jour férié.

Avec cette requête, il est également aisé de calculer le nombre de jours ouvrés en faisant TEMP.DATEFIN - TEMP.DATEDEBUT - JOURS_NON_OUVRES.

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
WITH
  TEMP AS
    (
      SELECT TO_DATE('10/03/2010 00:00:00', 'DD/MM/YYYY HH24:MI:SS') AS DATEDEBUT, TO_DATE('11/03/2010 00:00:00', 'DD/MM/YYYY HH24:MI:SS') AS DATEFIN FROM DUAL
      UNION ALL
      SELECT TO_DATE('10/03/2010 00:00:00', 'DD/MM/YYYY HH24:MI:SS') AS DATEDEBUT, TO_DATE('13/03/2010 12:00:00', 'DD/MM/YYYY HH24:MI:SS') AS DATEFIN FROM DUAL
      UNION ALL
      SELECT TO_DATE('10/03/2010 00:00:00', 'DD/MM/YYYY HH24:MI:SS') AS DATEDEBUT, TO_DATE('14/03/2010 18:00:00', 'DD/MM/YYYY HH24:MI:SS') AS DATEFIN FROM DUAL
      UNION ALL
      SELECT TO_DATE('10/03/2010 00:00:00', 'DD/MM/YYYY HH24:MI:SS') AS DATEDEBUT, TO_DATE('15/03/2010 12:00:00', 'DD/MM/YYYY HH24:MI:SS') AS DATEFIN FROM DUAL
      UNION ALL
      SELECT TO_DATE('13/03/2010 12:00:00', 'DD/MM/YYYY HH24:MI:SS') AS DATEDEBUT, TO_DATE('14/03/2010 12:00:00', 'DD/MM/YYYY HH24:MI:SS') AS DATEFIN FROM DUAL
      UNION ALL
      SELECT TO_DATE('14/03/2010 12:00:00', 'DD/MM/YYYY HH24:MI:SS') AS DATEDEBUT, TO_DATE('31/03/2010 06:00:00', 'DD/MM/YYYY HH24:MI:SS') AS DATEFIN FROM DUAL
      UNION ALL
      SELECT TO_DATE('12/03/2010 00:00:00', 'DD/MM/YYYY HH24:MI:SS') AS DATEDEBUT, TO_DATE('25/03/2010 12:00:00', 'DD/MM/YYYY HH24:MI:SS') AS DATEFIN FROM DUAL
      UNION ALL
      SELECT TO_DATE('14/03/2010 00:00:00', 'DD/MM/YYYY HH24:MI:SS') AS DATEDEBUT, TO_DATE('27/03/2010 00:00:00', 'DD/MM/YYYY HH24:MI:SS') AS DATEFIN FROM DUAL
      UNION ALL
      SELECT TO_DATE('08/03/2010 00:00:00', 'DD/MM/YYYY HH24:MI:SS') AS DATEDEBUT, TO_DATE('22/03/2010 00:00:00', 'DD/MM/YYYY HH24:MI:SS') AS DATEFIN FROM DUAL
      UNION ALL
      SELECT TO_DATE('12/03/2010 00:00:00', 'DD/MM/YYYY HH24:MI:SS') AS DATEDEBUT, TO_DATE('19/03/2010 12:30:59', 'DD/MM/YYYY HH24:MI:SS') AS DATEFIN FROM DUAL
      UNION ALL
      SELECT TO_DATE('01/05/2010 00:00:00', 'DD/MM/YYYY HH24:MI:SS') AS DATEDEBUT, TO_DATE('01/06/2010 00:00:00', 'DD/MM/YYYY HH24:MI:SS') AS DATEFIN FROM DUAL
      UNION ALL
      SELECT TO_DATE('13/05/2010 06:00:00', 'DD/MM/YYYY HH24:MI:SS') AS DATEDEBUT, TO_DATE('24/05/2010 18:00:00', 'DD/MM/YYYY HH24:MI:SS') AS DATEFIN FROM DUAL
      UNION ALL
      SELECT TO_DATE('22/12/2010 00:00:00', 'DD/MM/YYYY HH24:MI:SS') AS DATEDEBUT, TO_DATE('31/12/2010 12:00:00', 'DD/MM/YYYY HH24:MI:SS') AS DATEFIN FROM DUAL
    ),
 
  FERIE AS
    (
      SELECT TO_DATE('01/01/2010', 'DD/MM/YYYY') AS JOUR FROM DUAL
      UNION ALL
      SELECT TO_DATE('05/04/2010', 'DD/MM/YYYY') AS JOUR FROM DUAL
      UNION ALL
      SELECT TO_DATE('01/05/2010', 'DD/MM/YYYY') AS JOUR FROM DUAL
      UNION ALL
      SELECT TO_DATE('13/05/2010', 'DD/MM/YYYY') AS JOUR FROM DUAL
      UNION ALL
      SELECT TO_DATE('24/05/2010', 'DD/MM/YYYY') AS JOUR FROM DUAL
      UNION ALL
      SELECT TO_DATE('21/07/2010', 'DD/MM/YYYY') AS JOUR FROM DUAL
      UNION ALL
      SELECT TO_DATE('15/08/2010', 'DD/MM/YYYY') AS JOUR FROM DUAL
      UNION ALL
      SELECT TO_DATE('01/11/2010', 'DD/MM/YYYY') AS JOUR FROM DUAL
      UNION ALL
      SELECT TO_DATE('11/11/2010', 'DD/MM/YYYY') AS JOUR FROM DUAL
      UNION ALL
      SELECT TO_DATE('25/12/2010', 'DD/MM/YYYY') AS JOUR FROM DUAL
    )
 
SELECT
  TEMP.DATEDEBUT AS DATE_DEBUT,
  TEMP.DATEFIN AS DATE_FIN,
 
  --Nombre de samedis et dimanches des semaines complètes qu'il est possible de former entre les 2 dates.
  FLOOR((TEMP.DATEFIN - TEMP.DATEDEBUT) / 7) * 2
 
  --Durée en nombre de jours du premier week-end de la semaine restante.
  + GREATEST(LEAST(NEXT_DAY(TRUNC(TEMP.DATEDEBUT, 'DAY'), 'LUNDI'), TEMP.DATEFIN - (FLOOR((TEMP.DATEFIN - TEMP.DATEDEBUT) / 7) * 7)) - GREATEST(NEXT_DAY(TRUNC(TEMP.DATEDEBUT, 'DAY'), 'SAMEDI'), TEMP.DATEDEBUT), 0)
 
  --Durée en nombre de jours du deuxième week-end de la semaine restante.
  --Exemple : ce cas peut se présenter si la semaine restante commence le samedi 10/04/2010 à 22:00, et se termine le samedi 17/04/2010 à 08:00.
  + GREATEST(LEAST(NEXT_DAY(TRUNC(TEMP.DATEDEBUT + INTERVAL '7' DAY, 'DAY'), 'LUNDI'), TEMP.DATEFIN - (FLOOR((TEMP.DATEFIN - TEMP.DATEDEBUT) / 7) * 7)) - GREATEST(NEXT_DAY(TRUNC(TEMP.DATEDEBUT + INTERVAL '7' DAY, 'DAY'), 'SAMEDI'), TEMP.DATEDEBUT), 0)
 
  --Nombre de jours fériés qui ne sont ni un samedi ni un dimanche (car déjà comptabilisés).
  + NVL(SUM(GREATEST(LEAST(TRUNC(JOUR + INTERVAL '1' DAY, 'DD'), TEMP.DATEFIN) - GREATEST(TRUNC(JOUR, 'DD'), TEMP.DATEDEBUT), 0)), 0)
 
  AS JOURS_NON_OUVRES
FROM
  TEMP
  LEFT OUTER JOIN FERIE ON TO_CHAR(JOUR, 'D') NOT IN (6, 7)
GROUP BY
  TEMP.DATEDEBUT,
  TEMP.DATEFIN
Si ce code vous a été utile, un petit merci ferait plaisir à lire

Bien à vous

PS : si qqn sait comment éviter tous les UNION ALL que j'ai placé pour remplir les tables temporaires, je suis aussi preneur.