IV. Solutions - 4° partie▲
IV-A. Solution au problème n° 30 - Nombre de jours contigus▲
Comme nous l'avons dit, il faut s'aider d'une table des dates, comme ceci :
CREATE
TABLE
T_CALENDRIER_CLD
(
CLD_DATE DATE
NOT
NULL
PRIMARY
KEY
)
;
INSERT
INTO
T_CALENDRIER_CLD VALUES
(
'2003-12-30'
)
;
INSERT
INTO
T_CALENDRIER_CLD VALUES
(
'2003-12-31'
)
;
INSERT
INTO
T_CALENDRIER_CLD VALUES
(
'2004-01-01'
)
;
INSERT
INTO
T_CALENDRIER_CLD VALUES
(
'2004-01-02'
)
;
INSERT
INTO
T_CALENDRIER_CLD VALUES
(
'2004-01-03'
)
;
INSERT
INTO
T_CALENDRIER_CLD VALUES
(
'2004-01-04'
)
;
INSERT
INTO
T_CALENDRIER_CLD VALUES
(
'2004-01-05'
)
;
INSERT
INTO
T_CALENDRIER_CLD VALUES
(
'2004-01-06'
)
;
INSERT
INTO
T_CALENDRIER_CLD VALUES
(
'2004-01-07'
)
;
INSERT
INTO
T_CALENDRIER_CLD VALUES
(
'2004-01-08'
)
;
INSERT
INTO
T_CALENDRIER_CLD VALUES
(
'2004-01-09'
)
;
INSERT
INTO
T_CALENDRIER_CLD VALUES
(
'2004-01-10'
)
;
INSERT
INTO
T_CALENDRIER_CLD VALUES
(
'2004-01-11'
)
;
INSERT
INTO
T_CALENDRIER_CLD VALUES
(
'2004-01-12'
)
;
INSERT
INTO
T_CALENDRIER_CLD VALUES
(
'2004-01-13'
)
;
INSERT
INTO
T_CALENDRIER_CLD VALUES
(
'2004-01-14'
)
;
INSERT
INTO
T_CALENDRIER_CLD VALUES
(
'2004-01-15'
)
;
INSERT
INTO
T_CALENDRIER_CLD VALUES
(
'2004-01-16'
)
;
INSERT
INTO
T_CALENDRIER_CLD VALUES
(
'2004-01-17'
)
;
INSERT
INTO
T_CALENDRIER_CLD VALUES
(
'2004-01-18'
)
;
INSERT
INTO
T_CALENDRIER_CLD VALUES
(
'2004-01-19'
)
;
INSERT
INTO
T_CALENDRIER_CLD VALUES
(
'2004-01-20'
)
;
INSERT
INTO
T_CALENDRIER_CLD VALUES
(
'2004-01-21'
)
;
INSERT
INTO
T_CALENDRIER_CLD VALUES
(
'2004-01-22'
)
;
INSERT
INTO
T_CALENDRIER_CLD VALUES
(
'2004-01-23'
)
;
INSERT
INTO
T_CALENDRIER_CLD VALUES
(
'2004-01-24'
)
;
INSERT
INTO
T_CALENDRIER_CLD VALUES
(
'2004-01-25'
)
;
INSERT
INTO
T_CALENDRIER_CLD VALUES
(
'2004-01-26'
)
;
INSERT
INTO
T_CALENDRIER_CLD VALUES
(
'2004-01-27'
)
;
INSERT
INTO
T_CALENDRIER_CLD VALUES
(
'2004-01-28'
)
;
INSERT
INTO
T_CALENDRIER_CLD VALUES
(
'2004-01-29'
)
;
INSERT
INTO
T_CALENDRIER_CLD VALUES
(
'2004-01-30'
)
;
INSERT
INTO
T_CALENDRIER_CLD VALUES
(
'2004-01-31'
)
;
Voici une solution :
SELECT
T1.DateInterval AS
DateDebut,
CAST
((
SELECT
MIN
(
T2.DateInterval)
FROM
(
SELECT
DC1.PNG_DATE as
DateInterval, 'D'
as
TypeDate
FROM
T_CALENDRIER_CLD TD1
INNER
JOIN
T_CALENDRIER_CLD TD2
ON
TD1.CLD_DATE -
1
=
TD2.CLD_DATE
LEFT
OUTER
JOIN
T_PLANNING_PNG DC1
ON
DC1.PNG_DATE =
TD1.CLD_DATE
LEFT
OUTER
JOIN
T_PLANNING_PNG DC2
ON
DC2.PNG_DATE =
TD2.CLD_DATE
WHERE
DC1.PNG_DATE IS
NOT
NULL
AND
DC2.PNG_DATE IS
NULL
UNION
SELECT
DC1.PNG_DATE, 'F'
FROM
T_CALENDRIER_CLD TD1
INNER
JOIN
T_CALENDRIER_CLD TD2
ON
TD1.CLD_DATE +
1
=
TD2.CLD_DATE
LEFT
OUTER
JOIN
T_PLANNING_PNG DC1
ON
DC1.PNG_DATE =
TD1.CLD_DATE
LEFT
OUTER
JOIN
T_PLANNING_PNG DC2
ON
DC2.PNG_DATE =
TD2.CLD_DATE
WHERE
DC1.PNG_DATE IS
NOT
NULL
AND
DC2.PNG_DATE IS
NULL
)
T2
WHERE
TypeDate =
'F'
AND
T2.DateInterval >=
T1.DateInterval
GROUP
BY
T1.DateInterval )
AS
FLOAT
)
-
CAST
(
T1.DateInterval AS
FLOAT
)
+
1
AS
NbJours
FROM
(
SELECT
DC1.PNG_DATE as
DateInterval, 'D'
as
TypeDate
FROM
T_CALENDRIER_CLD TD1
INNER
JOIN
T_CALENDRIER_CLD TD2
ON
TD1.CLD_DATE -
1
=
TD2.CLD_DATE
LEFT
OUTER
JOIN
T_PLANNING_PNG DC1
ON
DC1.PNG_DATE =
TD1.CLD_DATE
LEFT
OUTER
JOIN
T_PLANNING_PNG DC2
ON
DC2.PNG_DATE =
TD2.CLD_DATE
WHERE
DC1.PNG_DATE IS
NOT
NULL
AND
DC2.PNG_DATE IS
NULL
UNION
SELECT
DC1.PNG_DATE, 'F'
FROM
T_CALENDRIER_CLD TD1
INNER
JOIN
T_CALENDRIER_CLD TD2
ON
TD1.CLD_DATE +
1
=
TD2.CLD_DATE
LEFT
OUTER
JOIN
T_PLANNING_PNG DC1
ON
DC1.PNG_DATE =
TD1.CLD_DATE
LEFT
OUTER
JOIN
T_PLANNING_PNG DC2
ON
DC2.PNG_DATE =
TD2.CLD_DATE
WHERE
DC1.PNG_DATE IS
NOT
NULL
AND
DC2.PNG_DATE IS
NULL
)
T1
WHERE
TypeDate =
'D'
Il va falloir maintenant l'expliquer en la décortiquant...
Tout d'abord, supposons que votre SGBDR soit capable de transformer une date en nombre, par exemple en nombre à virgule flottante (FLOAT) et que la différence entre deux dates ainsi transformées donne le nombre de jour... D'ailleurs si ce n'est pas le cas, il suffit d'utiliser le type INTERVAL de la norme SQL et faire une différence en jour pour que l'on revienne au même !
Ce point étant acquis, voyons comment notre requête est construite...
La première idée a été de calculer tous les débuts de périodes de jours consécutifs et la seconde la fin de ces mêmes périodes. Une fois cela traité il ne suffit plus que de calculer la différence en nombre de jours entre ces deux dates, à condition d'avoir mis en relation les bonnes périodes !
IV-A-1. Étape n°1 : On recherche les débuts de plage ayant des dates contiguës▲
SELECT
DC1.PNG_DATE as
DateInterval, 'D'
as
TypeDate
FROM
T_CALENDRIER_CLD TD1
INNER
JOIN
T_CALENDRIER_CLD TD2
ON
TD1.CLD_DATE -
1
=
TD2.CLD_DATE
LEFT
OUTER
JOIN
T_PLANNING_PNG DC1
ON
DC1.PNG_DATE =
TD1.CLD_DATE
LEFT
OUTER
JOIN
T_PLANNING_PNG DC2
ON
DC2.PNG_DATE =
TD2.CLD_DATE
WHERE
DC1.PNG_DATE IS
NOT
NULL
AND
DC2.PNG_DATE IS
NULL
;
Notez le
-1
dans la première jointure qui permet de voir le jour d'avant. Si votre SGBDR ne l'accepte pas, rectifiez la jointure comme suit :
ON
TD1.CLD_DATE -
1
DAY
=
TD2.CLD_DATE
Notez aussi le filtre pour lequel on recherche une date existante pour le planning avec le fait que dans ce même planning la date précédent n'existe pas !
Voici ce que donne cette requête :
DateInterval TypeDate
------------ --------
2004-01-01 D
2004-01-15 D
2004-01-30 D
Dans la colonne TypeDate, nous avons mis "D" pour début.
Notez qu'une autre variante, peut être plus performante consiste à utiliser le EXISTS :
SELECT
CLD_DATE as
DateInterval, 'D'
as
TypeDate
FROM
T_CALENDRIER_CLD TD
WHERE
NOT
EXISTS
(
SELECT
*
FROM
T_PLANNING_PNG
WHERE
PNG_DATE =
TD.CLD_DATE -
1
)
AND
EXISTS
(
SELECT
*
FROM
T_PLANNING_PNG
WHERE
PNG_DATE =
TD.CLD_DATE)
;
IV-A-2. Étape n° 2 : la seconde requête n'est guère plus complexe, pour trouver les fins de périodes▲
SELECT
DC1.PNG_DATE, 'F'
FROM
T_CALENDRIER_CLD TD1
INNER
JOIN
T_CALENDRIER_CLD TD2
ON
TD1.CLD_DATE +
1
=
TD2.CLD_DATE
LEFT
OUTER
JOIN
T_PLANNING_PNG DC1
ON
DC1.PNG_DATE =
TD1.CLD_DATE
LEFT
OUTER
JOIN
T_PLANNING_PNG DC2
ON
DC2.PNG_DATE =
TD2.CLD_DATE
WHERE
DC1.PNG_DATE IS
NOT
NULL
AND
DC2.PNG_DATE IS
NULL
;
PNG_DATE
------------------------------------------------------ ----
2004-01-03 00:00:00.000 F
2004-01-18 00:00:00.000 F
2004-01-30 00:00:00.000 F
IV-A-3. Étape n°3 : Voici maintenant le temps venu de joindre ces éléments... Cela semble plus facile à dire qu'à faire !▲
Commençons simple, en unionant ces deux requêtes :
SELECT
DC1.PNG_DATE as
DateInterval, 'D'
as
TypeDate
FROM
T_CALENDRIER_CLD TD1
INNER
JOIN
T_CALENDRIER_CLD TD2
ON
TD1.CLD_DATE -
1
=
TD2.CLD_DATE
LEFT
OUTER
JOIN
T_PLANNING_PNG DC1
ON
DC1.PNG_DATE =
TD1.CLD_DATE
LEFT
OUTER
JOIN
T_PLANNING_PNG DC2
ON
DC2.PNG_DATE =
TD2.CLD_DATE
WHERE
DC1.PNG_DATE IS
NOT
NULL
AND
DC2.PNG_DATE IS
NULL
UNION
SELECT
DC1.PNG_DATE, 'F'
FROM
T_CALENDRIER_CLD TD1
INNER
JOIN
T_CALENDRIER_CLD TD2
ON
TD1.CLD_DATE +
1
=
TD2.CLD_DATE
LEFT
OUTER
JOIN
T_PLANNING_PNG DC1
ON
DC1.PNG_DATE =
TD1.CLD_DATE
LEFT
OUTER
JOIN
T_PLANNING_PNG DC2
ON
DC2.PNG_DATE =
TD2.CLD_DATE
WHERE
DC1.PNG_DATE IS
NOT
NULL
AND
DC2.PNG_DATE IS
NULL
DateInterval TypeDate
--------------- --------
2004-01-01 D
2004-01-03 F
2004-01-15 D
2004-01-18 F
2004-01-30 D
2004-01-30 F
Nous ne sommes pas très avancés, car il va falloir synthétiser des paires de lignes différentes en une seule ligne.
Pour plus de compréhension, créons une vue à partir de cette requête :
CREATE
VIEW
V_DAT
AS
SELECT
DC1.PNG_DATE as
DateInterval, 'D'
as
TypeDate
FROM
T_CALENDRIER_CLD TD1
INNER
JOIN
T_CALENDRIER_CLD TD2
ON
TD1.CLD_DATE -
1
=
TD2.CLD_DATE
LEFT
OUTER
JOIN
T_PLANNING_PNG DC1
ON
DC1.PNG_DATE =
TD1.CLD_DATE
LEFT
OUTER
JOIN
T_PLANNING_PNG DC2
ON
DC2.PNG_DATE =
TD2.CLD_DATE
WHERE
DC1.PNG_DATE IS
NOT
NULL
AND
DC2.PNG_DATE IS
NULL
UNION
SELECT
DC1.PNG_DATE, 'F'
FROM
T_CALENDRIER_CLD TD1
INNER
JOIN
T_CALENDRIER_CLD TD2
ON
TD1.CLD_DATE +
1
=
TD2.CLD_DATE
LEFT
OUTER
JOIN
T_PLANNING_PNG DC1
ON
DC1.PNG_DATE =
TD1.CLD_DATE
LEFT
OUTER
JOIN
T_PLANNING_PNG DC2
ON
DC2.PNG_DATE =
TD2.CLD_DATE
WHERE
DC1.PNG_DATE IS
NOT
NULL
AND
DC2.PNG_DATE IS
NULL
Il est maintenant assez facile de mettre en correspondance les débuts de plage et les fins :
SELECT
T1.DateInterval AS
DateDebut,
(
SELECT
MIN
(
T2.DateInterval)
FROM
V_DAT T2
WHERE
TypeDate =
'F'
AND
T2.DateInterval >=
T1.DateInterval
GROUP
BY
T1.DateInterval )
As
DateFin
FROM
V_DAT T1
WHERE
TypeDate =
'D'
;
DateDebut DateFin
------------------ -------------------
2004-01-01 2004-01-03
2004-01-15 2004-01-18
2004-01-30 2004-01-30
IV-A-4. Étape n°4 : la suite, et donc la touche finale, est on ne peut plus simple puisqu'il s'agit de compter le nombre de jours de chaque période ainsi extraite !▲
SELECT
T1.DateInterval AS
DateDebut,
CAST
((
SELECT
MIN
(
T2.DateInterval)
FROM
V_DAT T2
WHERE
TypeDate =
'F'
AND
T2.DateInterval >=
T1.DateInterval
GROUP
BY
T1.DateInterval )
AS
FLOAT
)
-
CAST
(
T1.DateInterval AS
FLoat
)
+
1
As
Nb_jours
FROM
V_DAT T1
WHERE
TypeDate =
'D'
;
DateDebut Nb_jours
----------------- ----------------
2004-01-01 3.0
2004-01-15 4.0
2004-01-30 1.0
Bien entendu vous pouvez à nouveau substituer à la soustra id=""ction des dates transtypée en FLOAT, un calcul d'INTERVAL si votre SGBDR le supporte !
IV-B. Solution au problème n° 31 - plus proche valeur ▲
La valeur la plus proche signifie la valeur avec lequel l'écart entre la date souhaitée et l'ensemble des dates possible est minimum. Pour cela il faut calculer une différence en jours et comme cette différence peut se faire en plus ou en moins (avant ou après la date demandée) alors il faut obtenir la valeur absolue de cet écart.
Ceci se traduit par la requête SQL :
SELECT
*
FROM
T_RENDEZ_VOUS_RDV
WHERE
ABS
(
RDV_DATE -
CAST
(
'2008-01-12'
AS
DATE
)
AS
DAY
)
=
(
SELECT
MIN
(
ABS
(
RDV_DATE -
CAST
(
'2008-01-12'
AS
DATE
)
AS
DAY
))
FROM
T_RENDEZ_VOUS_RDV)
;
Qui donne bien la bonne réponse dans le cas ou la date est le 12 janvier 2008.
Cependant avec le 23 janvier 2008... tout change. Nous nous retrouvons avec :
RDV_DATE
---------------
2008-01-22
2008-01-24
Car il y a deux dates à égale distance de celle demandée.
Qu'à cela ne tienne, il suffit de rajouter que l'on veut celle d'avant ou d'après en appliquant un min ou un max !
La requête devient :
SELECT
MIN
(
RDV_DATE)
AS
RDV_DATE
FROM
T_RENDEZ_VOUS_RDV
WHERE
ABS
(
RDV_DATE -
CAST
(
'2008-01-12'
AS
DATE
)
AS
DAY
)
=
(
SELECT
MIN
(
ABS
(
RDV_DATE -
CAST
(
'2008-01-23'
AS
DATE
)
AS
DAY
))
FROM
T_RENDEZ_VOUS_RDV)
;
Voici cette même requête traduite pour MS SQL Server :
SELECT
MIN
(
RDV_DATE)
AS
RDV_DATE
FROM
T_RENDEZ_VOUS_RDV
WHERE
ABS
(
DATEDIFF
(
day
, RDV_DATE, CAST
(
'2008-01-23'
id=
""
AS
DATETIME
)))
=
(
SELECT
MIN
(
ABS
(
DATEDIFF
(
day
, RDV_DATE, CAST
(
'2008-01-12'
AS
DATETIME
))))
FROM
T_RENDEZ_VOUS id=
""
_RDV)
;
IV-C. Solution au problème n° 32 - primes de salaires▲
Tout d'abord, pour "éclater" la prime sur le nombre d'échéances il nous faut une table des nombres de 1 à n.
CREATE
TABLE
T_NUM
(
NUM INT
)
;
INSERT
INTO
T_NUM VALUES
(
1
)
;
INSERT
INTO
T_NUM VALUES
(
2
)
;
INSERT
INTO
T_NUM VALUES
(
3
)
;
INSERT
INTO
T_NUM VALUES
(
4
)
;
INSERT
INTO
T_NUM VALUES
(
5
)
;
INSERT
INTO
T_NUM VALUES
(
6
)
;
INSERT
INTO
T_NUM VALUES
(
7
)
;
INSERT
INTO
T_NUM VALUES
(
8
)
;
INSERT
INTO
T_NUM VALUES
(
9
)
;
INSERT
INTO
T_NUM VALUES
(
10
)
;
INSERT
INTO
T_NUM VALUES
(
11
)
;
INSERT
INTO
T_NUM VALUES
(
12
)
;
Une première impression est donnée par la requête suivante :
SELECT
PRM_EMPLOYE, CAST
(
PRM_MONTANT /
PRN_NB_ECHEANCES AS
DECIMAL
(
16
, 2
))
AS
PART_PRIME, NUM
FROM
T_PRIME_PRM
INNER
JOIN
T_NUM
ON
NUM BETWEEN
1
AND
PRN_NB_ECHEANCES
PRM_EMPLOYE PART_PRIME NUM
---------------- ------------------ -----------
JAK 1000.00 1
POL 333.33 1
POL 333.33 2
POL 333.33 3
LUC 83.33 1
LUC 83.33 2
LUC 83.33 3
LUC 83.33 4
LUC 83.33 5
LUC 83.33 6
LUC 83.33 7
LUC 83.33 8
LUC 83.33 9
LUC 83.33 10
LUC 83.33 11
LUC 83.33 12
Il suffit d'ajouter à cette table l'écart constaté entre la somme des échéances et la prime originelle. Voici comment on peut calculer cet écart :
SELECT
PRM_EMPLOYE, CAST
(
PRM_MONTANT /
PRN_NB_ECHEANCES AS
DECIMAL
(
16
, 2
))
AS
PART_PRIME, NUM,
PRM_MONTANT -
(
SELECT
MAX
(
NUM)
*
(
CAST
(
PRM_MONTANT /
PRN_NB_ECHEANCES AS
DECIMAL
(
16
, 2
))
)
FROM
T_PRIME_PRM
INNER
JOIN
T_NUM
ON
NUM BETWEEN
1
AND
PRN_NB_ECHEANCES
WHERE
P.PRM_EMPLOYE =
PRM_EMPLOYE
GROUP
BY
PRM_MONTANT, PRN_NB_ECHEANCES)
as
ECART
FROM
T_PRIME_PRM P
INNER
JOIN
T_NUM
ON
NUM BETWEEN
1
AND
PRN_NB_ECHEANCES
PRM_EMPLOYE PART_PRIME NUM ECART
---------------- ------------------ ----------- ------------------------------
JAK 1000.00 1 .00
POL 333.33 1 .01
POL 333.33 2 .01
POL 333.33 3 .01
LUC 83.33 1 .04
LUC 83.33 2 .04
LUC 83.33 3 .04
LUC 83.33 4 .04
LUC 83.33 5 .04
LUC 83.33 6 .04
LUC 83.33 7 .04
LUC 83.33 8 .04
LUC 83.33 9 .04
LUC 83.33 10 .04
LUC 83.33 11 .04
LUC 83.33 12 .04
Simplifions-nous la vie pour la suite en transformant cette requête en vue :
CREATE
VIEW
V_PRIMES
AS
SELECT
PRM_EMPLOYE, CAST
(
PRM_MONTANT /
PRN_NB_ECHEANCES AS
DECIMAL
(
16
, 2
))
AS
PART_PRIME, NUM,
PRM_MONTANT -
(
SELECT
MAX
(
NUM)
*
(
CAST
(
PRM_MONTANT /
PRN_NB_ECHEANCES AS
DECIMAL
(
16
, 2
))
)
FROM
T_PRIME_PRM
INNER
JOIN
T_NUM
ON
NUM BETWEEN
1
AND
PRN_NB_ECHEANCES
WHERE
P.PRM_EMPLOYE =
PRM_EMPLOYE
GROUP
BY
PRM_MONTANT, PRN_NB_ECHEANCES)
as
ECART
FROM
T_PRIME_PRM P
INNER
JOIN
T_NUM
ON
NUM BETWEEN
1
AND
PRN_NB_ECHEANCES ;
Dès lors, on peut maintenant placer la prime au niveau de la dernière échéance, comme suit :
SELECT
PRM_EMPLOYE, PART_PRIME, NUM,
(
SELECT
ECART
FROM
V_PRIMES
WHERE
PRM_EMPLOYE =
P1.PRM_EMPLOYE
AND
PART_PRIME =
P1.PART_PRIME
GROUP
BY
PART_PRIME, PRM_EMPLOYE, ECART
HAVING
MAX
(
NUM)
=
P1.NUM)
REPORT_ECART
FROM
V_PRIMES P1 ;
PRM_EMPLOYE PART_PRIME NUM REPORT_ECART
---------------- ------------------ ----------- ------------------------------
JAK 1000.00 1 .00
POL 333.33 1 NULL
POL 333.33 2 NULL
POL 333.33 3 .01
LUC 83.33 1 NULL
LUC 83.33 2 NULL
LUC 83.33 3 NULL
LUC 83.33 4 NULL
LUC 83.33 5 NULL
LUC 83.33 6 NULL
LUC 83.33 7 NULL
LUC 83.33 8 NULL
LUC 83.33 9 NULL
LUC 83.33 10 NULL
LUC 83.33 11 NULL
LUC 83.33 12 .04
Il ne suffit plus maintenant que d'une addition, avec dénullification de l'écart !
SELECT
PRM_EMPLOYE, NUM, PART_PRIME +
COALESCE
((
SELECT
ECART
FROM
V_PRIMES
WHERE
PRM_EMPLOYE =
P1.PRM_EMPLOYE
AND
PART_PRIME =
P1.PART_PRIME
GROUP
BY
PART_PRIME, PRM_EMPLOYE, ECART
HAVING
MAX
(
NUM)
=
P1.NUM)
, 0
)
AS
PART_PRIME_EXACTE
FROM
V_PRIMES P1 ;
PRM_EMPLOYE NUM PART_PRIME_EXACTE
---------------- ----------- -------------------------------
JAK 1 1000.00
POL 1 333.33
POL 2 333.33
POL 3 333.34
LUC 1 83.33
LUC 2 83.33
LUC 3 83.33
LUC 4 83.33
LUC 5 83.33
LUC 6 83.33
LUC 7 83.33
LUC 8 83.33
LUC 9 83.33
LUC 10 83.33
LUC 11 83.33
LUC 12 83.37
Sans l'apport de la vue, la requête s'avère un peu indigeste :
SELECT
PRM_EMPLOYE, NUM, PART_PRIME +
COALESCE
((
SELECT
ECART
FROM
(
SELECT
PRM_EMPLOYE, CAST
(
PRM_MONTANT /
PRN_NB_ECHEANCES AS
DECIMAL
(
16
, 2
))
AS
PART_PRIME, NUM,
PRM_MONTANT -
(
SELECT
MAX
(
NUM)
*
(
CAST
(
PRM_MONTANT /
PRN_NB_ECHEANCES AS
DECIMAL
(
16
, 2
))
)
FROM
T_PRIME_PRM
INNER
JOIN
T_NUM
ON
NUM BETWEEN
1
AND
PRN_NB_ECHEANCES
WHERE
P.PRM_EMPLOYE =
PRM_EMPLOYE
GROUP
BY
PRM_MONTANT, PRN_NB_ECHEANCES)
as
ECART
FROM
T_PRIME_PRM P
INNER
JOIN
T_NUM
ON
NUM BETWEEN
1
AND
PRN_NB_ECHEANCES)
AS
T
WHERE
PRM_EMPLOYE =
P1.PRM_EMPLOYE
AND
PART_PRIME =
P1.PART_PRIME
GROUP
BY
PART_PRIME, PRM_EMPLOYE, ECART
HAVING
MAX
(
NUM)
=
P1.NUM)
, 0
)
AS
PART_PRIME_EXACTE
FROM
(
SELECT
PRM_EMPLOYE, CAST
(
PRM_MONTANT /
PRN_NB_ECHEANCES AS
DECIMAL
(
16
, 2
))
AS
PART_PRIME, NUM,
PRM_MONTANT -
(
SELECT
MAX
(
NUM)
*
(
CAST
(
PRM_MONTANT /
PRN_NB_ECHEANCES AS
DECIMAL
(
16
, 2
))
)
FROM
T_PRIME_PRM
INNER
JOIN
T_NUM
ON
NUM BETWEEN
1
AND
PRN_NB_ECHEANCES
WHERE
P.PRM_EMPLOYE =
PRM_EMPLOYE
GROUP
BY
PRM_MONTANT, PRN_NB_ECHEANCE id=
""
S)
as
ECART
FROM
T_PRIME_PRM P
INNER
JOIN
T_NUM
ON
NUM BETWEEN
1
AND
PRN_NB_ECH id=
""
EANCES)
P1 ;
IV-D. Solution au problème n° 33 - affectations comptables▲
Une première idée est de réaliser l'affectation en réalisant un rapprochement entre compte et "masque" de compte à l'aide de l'opérateur LIKE. C'est d'ailleurs pour cela que j'ai remplacé les ? par des _ ...
SELECT
CPT_COMPTE, CPT_ANA, CPT_MONTANT, AFC_AFFECT, AFC_COMPTE, AFC_ANA
FROM
T_COMPTE_CPT C
INNER
JOIN
T_AFFECTATION_AFC A
ON
CPT_COMPTE LIKE
AFC_COMPTE
AND
CPT_ANA LIKE
AFC_ANA
ORDER
BY
1
, 2
;
CPT_COMPTE CPT_ANA CPT_MONTANT AFC_AFFECT AFC_COMPTE AFC_ANA
---------- ------- ------------------ ---------- ---------- -------
612000 12345 130.40 T124 6_____ _____
612000 12345 130.40 T125 612___ _____ -->(Regle 612??? ?????)
612000 26045 260.50 T125 612___ _____ -->(Règle 612??? ?????)
612000 26045 260.50 T124 6_____ _____
612000 26045 260.50 T033 6_____ 26___
612101 33556 330.50 T028 6_____ __5__
612101 33556 330.50 T124 6_____ _____
612101 33556 330.50 T125 612___ _____
612101 33556 330.50 T126 612___ __5__ -->(Règle 612??? ??5??)
654000 26346 33.55 T124 6_____ _____
654000 26346 33.55 T033 6_____ 26___ -->(Règle 6????? 26???)
654102 28333 180.10 T124 6_____ _____ -->(Règle 6????? ?????)
Comme nous le constatons, cette méthode nous donne différentes possibilités d'affectation des lignes comptables aux comptes. Il faut donc trouver une astuce pour faire en sorte que l'affectation soit la plus complète possible.
Demandons nous quelle est la règle sous-jacente au terme "meilleur" ?
Visiblement il s'agit de considérer que les chiffres les plus à gauche sont plus importants que les chiffres les plus à droite.
Dans ce cas, il faut trouver un moyen de valuer la complétude du masque de compte.
C'est généralement par le calcul d'un "poids" que l'on s'en tire. Un poids, c'est un facteur, un coefficient qui indique que telle information est plus sûre que telle autre.
Dans notre cas nous pouvons donner un poids à l'aide de puissances de 2. Si le caractère est présent alors la puissance du rang du caractère est prise en compte. Sinon, c'est un zéro. Finalement on fait la somme de toutes ces puissances afin de donner le poids globale du masque de compte.
Ainsi pour : 612_ _ _ _ _5_ _, le poids sera le suivant :
rang 2^rang-1 caractère prise en compte total
------ ---------- ----------- ------------------ ----------
11 1024 6 oui 1024
10 512 1 oui 512
9 256 2 oui 256
8 128 _ non 0
7 64 _ non 0
6 32 _ non 0
5 16 _ non 0
4 8 _ non 0
3 4 5 oui 4
2 2 _ non 0
1 1 _ non 0
===========
TOTAL 1796
On peut donc calculer chaque poids de chaque masque et ajouter cela à la table T_AFFECTATION_AFC en lui adjoignant une nouvelle colonne :
ALTER
TABLE
T_AFFECTATION_AFC
ADD
AFC_PRIORITE SMALLINT
;
Il faut ensuite mettre à jour ligne à ligne la table des affectations, comme ceci :
UPDATE
T_AFFECTATION_AFC SET
AFC_PRIORITE =
1024
WHERE
AFC_AFFECT =
'T124'
;
UPDATE
T_AFFECTATION_AFC SET
AFC_PRIORITE =
1028
WHERE
AFC_AFFECT =
'T028'
;
UPDATE
T_AFFECTATION_AFC SET
AFC_PRIORITE =
1048
WHERE
AFC_AFFECT =
'T033'
;
UPDATE
T_AFFECTATION_AFC SET
AFC_PRIORITE =
1792
WHERE
AFC_AFFECT =
'T125'
;
UPDATE
T_AFFECTATION_AFC SET
AFC_PRIORITE =
1796
WHERE
AFC_AFFECT =
'T126'
;
Bien entendu, certaines personnes vont dire que je triche puisque j'ajoute à la table d'affectation une colonne... Mais tous d'abord disons que si nous violons la règle, nous n'en violons pas l'esprit qui consiste à préserver l'intégrité de la base de données. Souvenez vous de la règle de Codd n°9, intitulée " Indépendance logique des données " : les applications et les programmes terminaux sont logiquement inaffectés, quand des changements de tous ordres, préservant les informations et qui ne leur portent théoriquement aucune atteinte, sont apportés aux tables de base (restructuration). En particulier l'ajout d'une table, d'une vue ou d'une colonne à une table existante ne modifie en rien le comportement des applications qui utilisent cette base de données (ou tout du moins, cela devrait être vrai !).
Cependant et pour les puriste nous verrons en final comment respecter notre serment et montrerons une solution sans restructuration de l'existant ni ajout d'une fonction utilisateur.
Parce que tant qu'à violer nos propres règles, allons-y franchement en utilisant une UDF afin de faciliter le calculs des poids. Je vous la livre écrite pour MS SQL Server :
CREATE
FUNCTION
F_POIDS_COMPTE (
@DATA
VARCHAR
(
32
))
RETURNS
INTEGER
AS
BEGIN
IF
@DATA
IS
NULL
RETURN
NULL
;
DECLARE
@OUT
INT
, @I INT
;
SELECT
@OUT
=
0
, @I =
LEN(
@DATA
)
;
WHILE
@I >=
1
BEGIN
IF
SUBSTRING
(
@DATA
, @I, 1
)
<>
'_'
SET
@OUT
=
@OUT
+
POWER
(
2
, LEN(
@DATA
)
-
@I)
;
SET
@I =
@I -
1
;
END
;
RETURN
@OUT
;
END
;
Dès lors et afin de départager nos différentes possibilité d'affectation, il ne s'agit plus que de retenir chaque ligne ayant le poids le plus fort, ce qui peut se traduire par :
SELECT
CPT_COMPTE, CPT_ANA, CPT_MONTANT, AFC_AFFECT
FROM
T_COMPTE_CPT C
INNER
JOIN
T_AFFECTATION_AFC A
ON
CPT_COMPTE LIKE
AFC_COMPTE
AND
CPT_ANA LIKE
AFC_ANA
WHERE
AFC_PRIORITE =
(
SELECT
MAX
(
AFC_PRIORITE)
FROM
T_COMPTE_CPT C2
INNER
JOIN
T_AFFECTATION_AFC A2
ON
CPT_COMPTE LIKE
AFC_COMPTE
AND
CPT_ANA LIKE
AFC_ANA
WHERE
C2.CPT_COMPTE =
C.CPT_COMPTE
AND
C2.CPT_ANA =
C.CPT_ANA
AND
C2.CPT_MONTANT =
C.CPT_MONTANT)
;
Et qui donne le bon résultat :
CPT_COMPTE CPT_ANA CPT_MONTANT AFC_AFFECT
---------- ------- ------------------ ----------
654102 28333 180.10 T124
654000 26346 33.55 T033
612101 33556 330.50 T126
612000 26045 260.50 T125
612000 12345 130.40 T125
Comment nous débarrasser de la colonne en sus dans la table des affectations ?
Simple, il suffit de rajouter une table avec toutes les données nécessaires... Cela ne nous est pas interdit !
CREATE
TABLE
T_AFFECTATION2_AFC
(
AFC_COMPTE CHAR
(
6
)
,
AFC_ANA CHAR
(
5
)
,
AFC_AFFECT CHAR
(
4
)
,
AFC_PRIORITE SMALLINT
)
;
INSERT
INTO
T_AFFECTATION2_AFC VALUES
(
'6_____'
, '_____'
, 'T124'
, 1024
)
;
INSERT
INTO
T_AFFECTATION2_AFC VALUES
(
'6_____'
, '__5__'
, 'T028'
, 1028
)
;
INSERT
INTO
T_AFFECTATION2_AFC VALUES
(
'6_____'
, '26___'
, 'T033'
, 1048
)
;
INSERT
INTO
T_AFFECTATION2_AFC VALUES
(
'612___'
, '_____'
, 'T125'
, 1792
)
;
INSERT
INTO
T_AFFECTATION2_AFC VALUES
(
'612___'
, '__5__'
, 'T126'
, 1796
)
;
Et au fait... comment calculer les poids sans utiliser une UDF ?
Avec une requête... tout simplement !
SELECT
AFC_COMPTE, AFC_ANA, SUM
(
POID)
AS
POIDS
FROM
(
SELECT
AFC_COMPTE, AFC_ANA,
CASE
WHEN
SUBSTRING
(
AFC_COMPTE+
AFC_ANA, NUM, 1
)
=
'_'
THEN
0
ELSE
POWER
(
2
, LEN(
AFC_COMPTE+
AFC_ANA)-
NUM)
END
AS
POID
FROM
T_AFFECTATION_AFC
INNER
JOIN
T_NUM
ON
NUM BETWEEN
1
AND
LEN(
AFC_COMPTE+
AFC_ANA))
T
GROUP
BY
AFC_COMPTE, AFC_ANA
Pour la mystérieuse table T_NUM qui apparait bizarrement ici, souvenez vous qu'elle apparait dans de nombreux exercices et de trouve définit comme suit :
CREATE
TABLE
T_NUM (
NUM INT
)
;
INSERT
INTO
T_NUM VALUES
(
1
)
;
INSERT
INTO
T_NUM VALUES
(
2
)
;
INSERT
INTO
T_NUM VALUES
(
3
)
;
INSERT
INTO
T_NUM VALUES
(
4
)
;
INSERT
INTO
T_NUM VALUES
(
5
)
;
INSERT
INTO
T_NUM VALUES
(
6
)
;
INSERT
INTO
T_NUM VALUES
(
7
)
;
INSERT
INTO
T id=
""
_NUM VALUES
(
8
)
;
INSERT
INTO
T_NUM VALUES
(
9
)
;
INSERT
INTO
T_NUM VALUES
(
10
)
;
INSERT
INTO
T_NUM VALUES
(
11
)
;
INSERT
INTO
T_NUM id=
""
VALUES
(
12
)
;
...
IV-E. Solution au problème n° 33 - précédents (ou suivants)▲
Commençons par numéroter les lignes de cette table en utilisant une inéqui auto jointure avec comptage :
SELECT
T1.PSV, COUNT
(*)
AS
N
FROM
T_PRECSUIV_PSV T1
INNER
JOIN
T_PRECSUIV_PSV T2
ON
T1.PSV >=
T2.PSV
GROUP
BY
T1.PSV
PSV N
---- -----------
AAA 1
BBB 2
CCC 3
DDD 4
Dès lors nous savons que la précédente est celle ayant un comptage de -1 par rapport à N, la solution devient triviale :
SELECT
T1.PSV, COUNT
(*)
AS
N, COUNT
(*)
-
1
AS
PRECEDENT
FROM
T_PRECSUIV_PSV T1
INNER
JOIN
T_PRECSUIV_PSV T2
ON
T1.PSV >=
T2.PSV
GROUP
BY
T1.PSV
PSV N PRECEDENT
---- ----------- -----------
AAA 1 0
BBB 2 1
CCC 3 2
DDD 4 3
Sauf que la ligne n° 0 n'existant pas, il nous faut faire :
SELECT
T1.PSV, COUNT
(*)
AS
N, NULLIF
(
COUNT
(*)
-
1
, 0
)
AS
PRECEDENT
FROM
T_PRECSUIV_PSV T1
INNER
JOIN
T_PRECSUIV_PSV T2
ON
T1.PSV >=
T2.PSV
GROUP
BY
T1.PSV
PSV N PRECEDENT
---- ----------- -----------
AAA 1 NULL
BBB 2 1
CCC 3 2
DDD 4 3
Ce qui est une bonne solution.
En définitive, la solution globale est fournie par la requête suivante :
SELECT
PSV1 AS
VALEUR_ACTUELLE, PSV2 AS
VALEUR PRECEDENTE
FROM
(
SELECT
T1.PSV AS
PSV1, COUNT
(
T1.PSV)
AS
N1
FROM
T_PRECSUIV_PSV T1
LEFT
OUTER
JOIN
T_PRECSUIV_PSV T2
ON
T1.PSV >=
T2.PSV
GROUP
BY
T1.PSV)
AS
T1
LEFT
OUTER
JOIN
(
SELECT
T1.PSV AS
PSV2, COUNT
(
T1.PSV)
+
1
AS
N2
FROM
T_PRECSUIV_PSV T1
LEFT
OUTER
JOIN
T_PRECSUIV_PSV T2
ON
T1.PSV >=
T2.PSV
GROUP
BY
T1.PSV)
T2
ON
T1. N1 =
T2.N2
VALEUR_ACTUELLE VALEUR PRECEDENTE
--------------- -----------------
AAA NULL
BBB AAA
CCC BBB
DDD CCC
Mais cette solution est-elle suffisante ?
Par exemple, rajoutons à notre table les données suivantes :
INSERT
INTO
T_PRECSUIV_PSV VALUES
(
'BBB'
)
;
INSERT
INTO
T_PRECSUIV_PSV VALUES
(
'CCC'
)
;
INSERT
INTO
T_PRECSUIV_PSV VALUES
(
'CCC'
)
;
INSERT
INTO
T_PRECSUIV_PSV VALUES
(
'CCC'
)
;
Notre requête précédente donne :
VALEUR_ACTUELLE VALEUR_PRECEDENT
--------------- ----------------
AAA NULL
BBB NULL
CCC NULL
DDD NULL
Tentons une autre approche et essayons de chercher pour chaque ligne la valeur immédiatement inférieure.
Cela peut se traduire par :
SELECT
DISTINCT
PSV AS
VALEUR_ACTUELLE,
(
SELECT
MAX
(
PSV)
FROM
T_PRECSUIV_PSV
WHERE
PSV <
T.PSV)
AS
VALEUR_PRECEDENTE
FROM
T_PRECSUIV_PSV AS
T ;
VALEUR_ACTUELLE VALEUR_PRECEDENTE
--------------- ---------------
AAA NULL
BBB AAA
CCC BBB
DDD CCC
Cette solution, apparemment simpliste gagne en force et en souplesse !
Bien entendu le chaînage avant/arrière (précédent/suivant) est facile à réaliser :
SELECT
DISTINCT
PSV AS
VALEUR_ACTUELLE,
(
SELECT
MAX
(
PSV)
FROM
T_PRECSUIV_PSV
WHERE
PSV <
T.PSV)
AS
VALEUR_PRECEDENTE,
(
SELECT
MIN
(
PSV)
FROM
T_PRECSUIV_PSV
WHERE
PSV >
T.PSV)
AS
VALEUR_SUIVANTE
FROM
T_PRECSUIV_PSV AS
T ;
VALEUR_ACTUELLE VALEUR_PRECEDENTE VALEUR_SUIVANTE
--------------- ----------------- ---------------
A id=""AA NULL BBB
BBB AAA CCC
CCC BBB DDD
DDD id="" CCC NULL
IV-F. Solution au problème n° 35 - matchs et victoires▲
Obtenir le nombre de matches des joueurs relève d'une simple astuce de jointure pour le comptage. Une jointure conditionnelle avec un OR dans la condition de jointure. C'est ce que l'on appelle une non équi jointure :
SELECT
JOR_NOM, COUNT
(*)
AS
NB_MATCHS
FROM
T_MATCH_MCH M
INNER
JOIN
T_JOUEUR_JOR J
ON
J.JOR_ID =
M.JOR_ID1 OR
J.JOR_ID =
M.JOR_ID2
GROUP
BY
JOR_NOM ;
JOR_NOM NB_MATCHS
-------------------------------- -----------
Camus 2
Dupont 3
Mercier 3
Obtenir le nombre de victoire des joueurs relève d'une autre astuce de jointure presque aussi simple :
SELECT
JOR_NOM, COUNT
(*)
AS
NB_VICTOIRE
FROM
T_MATCH_MCH M
INNER
JOIN
T_JOUEUR_JOR J
ON
J.JOR_ID =
CASE
WHEN
MCH_SCORE_JOUEUR1 >
MCH_SCORE_JOUEUR2 THEN
M.JOR_ID1
WHEN
MCH_SCORE_JOUEUR2 >
MCH_SCORE_JOUEUR1 THEN
M.JOR_ID2
END
GROUP
BY
JOR_NOM ;
JOR_NOM NB_VICTOIRE
-------------------------------- -----------
Camus 2
Dupont 1
Mercier 1
En fait la touche finale est bien plus simple qu'il n'y parait, car il suffit de mettre ces deux précédentes requêtes en tant que table dérivées dans la clause FROM d'une super requête et de les joindre sur le nom du joueur.
Mais comme il peut se trouver des homonymes, nous allons ajouter à nos deux requête l'identifiant du joueur et réaliser la jointure dessus, comme ceci :
SELECT
T1.JOR_ID, T1.JOR_NOM, NB_MATCHS, NB_VICTOIRE
FROM
(
SELECT
JOR_ID, JOR_NOM, COUNT
(*)
AS
NB_MATCHS
FROM
T_MATCH_MCH M
INNER
JOIN
T_JOUEUR_JOR J
ON
J.JOR_ID =
M.JOR_ID1 OR
J.JOR_ID =
M.JOR_ID2
GROUP
BY
JOR_ID, JOR_NOM)
AS
T1
LEFT
OUTER
JOIN
(
SELECT
JOR_ID, JOR_NOM, COUNT
(*)
AS
NB_VICTOIRE
FROM
T_MATCH_MCH M
INNER
JOIN
T_JOUEUR_JOR J
ON
J.JOR_ID =
CASE
WHEN
MCH_SCORE_JOUEUR1 >
MCH_SCORE_JOUEUR2 THEN
M.JOR_ID1
WHEN
MCH_SCORE_JOUEUR2 >
MCH_SCORE_JOUEUR1 THEN
M.JOR_ID2
END
GROUP
BY
JOR_ID, JOR_NOM)
AS
T2
ON
T1.JOR_ID =
T2.JOR_ID
JOR_ID JOR_NOM NB_MATCHS NB_VICTOIRE
----------- -------------------------------- ----------- -----------
2 Camus 2 2
1 Dupont 3 1
3 Mercier 3 1
id="" Cependant, je vous laisse deviner pourquoi j'ai préféré une jointure externe gauche à une jointure id="" interne !
IV-G. Solution au problème n° 36 - tri bâtard▲
Une idée serait de faire un transtypage en entier et de forcer la clause de tri ORDER BY à opérer sur cette transformation, comme ceci :
SELECT
*
FROM
T_PARCELLE_PCL
ORDER
BY
CAST
(
PCL_NUM AS
INTEGER
)
;
Cependant, cette requête part en erreur du fait de la présence de caractères non numériques...
En fait, l'astuce consiste à effectuer un double tri :
- premièrement sur la longueur de la donnée ;
- deuxièmement sur la donnée elle même.
SELECT
*
FROM
T_PARCELLE_PCL
ORDER
BY
CHARACTER_LENGTH
(
PCL_NUM)
, PCL_NUM ;
PCL_NUM
id=""----------------
1
2
3
11
21
22
111
221
28A
28B
Et le id="" tout est joué !
IV-H. Solution au problème n° 37 - vote contraint ▲
Un savant calcul combinant la multiplication et la dénullification est, semble-t-il, la chose la plus courte possible.
ALTER
TABLE
T_SCHNORKELZIG_SKZ ADD
CONSTRAINT
CK
CHECK
((
COALESCE
(
SKZ_MARC+
1
,1
)*
COALESCE
(
SKZ_PAUL+
1
,1
)*
COALESCE
(
SKZ_JEAN+
1
,1
)*
COALESCE
(
SKZ_LEON+
1
,1
))
IN
(
2
,6
,24
,120
))
Remarquez que dans le IN figurent des nombres issus de la fonction factorielle... Pour y parvenir et comme la multiplication par 1 ne change pas les données de départ, on ajoute 1 à la valeur du vote et on met 1 en cas de NULL. Ainsi, les combinaisons possibles sont :
2 * 3 * 4 * 5 = 120
2 * 3 * 4 = 24
2 * 3 = 6
2 = 2
Notez que la longueur de la clause à partir du mot CHECK fait 114 caractères... id="" Avez-vous fait moins ?
IV-I. Solution au problème n° 38 - propriété bien gardée▲
La solution à ce problème fait partie des requêtes les plus complexes à élaborer. En général les requêtes de manipulation du temps et en particulier celle portant sur des intervalles (durées); sont très difficile à mettre au point.
Voici cependant la décomposition de cette requête. Nous allons d'abord voire une solution globale sous forme de CTE puis nous allons la décortiquer petit à petit pour en comprendre son mécanisme.
IV-I-1. Étape préliminaire▲
Tout d'abord il nous faut rajouter quelque information à notre ensemble de données : en effet nous devons borner l'intervalle de traitement de nos données entre le 1er septembre 2008 à 0h et le 7 septembre 2008 à 23h59'59". Pour cela nous devons soit couper les périodes chevauchant ces limites, soit si aucune période ne les chevauche, les créer de toute pièce avec une durée nulle.
CREATE
TABLE
T_BORNE_BRN (
DEBUT DATETIME
, FIN DATETIME
)
INSERT
INTO
T_BORNE_BRN VALUES
(
'20080901 00:00:00'
, '20080901 00:00:00'
)
INSERT
INTO
T_BORNE_BRN VALUES
(
'20080907 23:59:59'
, '20080907 23:59:59'
)
La solution sous forme de CTE :
WITH
V_GDN
-- Les périodes nouvelles de gardiennage avec les périodes bornes recoupées sur la durée de l'intervalle à scruter
AS
(
SELECT
CASE
WHEN
GDN_DATEHEURE_DEBUT <
(
SELECT
MIN
(
DEBUT)
FROM
T_BORNE_BRN)
THEN
(
SELECT
MIN
(
DEBUT)
FROM
T_BORNE_BRN)
ELSE
GDN_DATEHEURE_DEBUT
END
AS
GDN_DATEHEURE_DEBUT,
CASE
WHEN
GDN_DATEHEURE_FIN >
(
SELECT
MAX
(
DEBUT)
FROM
T_BORNE_BRN)
THEN
(
SELECT
MAX
(
DEBUT)
FROM
T_BORNE_BRN)
ELSE
GDN_DATEHEURE_FIN
END
AS
GDN_DATEHEURE_FIN
FROM
dbo.T_GARDIENNAGE_GDN
UNION
ALL
SELECT
DEBUT, FIN
FROM
T_BORNE_BRN
)
,
-- calcul des périodes agrégées de gardiennage, c'est à dire quelles sont les périodes pendant lesquelles il y a continuellement quelqu'un ?
GDT
AS
-- on combine toutes les heures de début avec toutes les heures de fin qui sont supérieures à celle de début
-- on réalise en fait une matrice "triangulaire"
(
SELECT
DISTINCT
GD1.GDN_DATEHEURE_DEBUT, GD2.GDN_DATEHEURE_FIN
FROM
V_GDN GD1
INNER
JOIN
V_GDN GD2
ON
GD1.GDN_DATEHEURE_DEBUT <=
GD2.GDN_DATEHEURE_FIN
-- élimination des trous entre GD1.GDN_DATEHEURE_DEBUT et GD2.GDN_DATEHEURE_FIN
AND
NOT
EXISTS
(
SELECT
*
FROM
V_GDN GD3
WHERE
GD3.GDN_DATEHEURE_DEBUT >
GD1.GDN_DATEHEURE_FIN
AND
GD3.GDN_DATEHEURE_DEBUT <=
GD2.GDN_DATEHEURE_DEBUT
AND
NOT
EXISTS
(
SELECT
*
FROM
V_GDN GD4
WHERE
GD4.GDN_DATEHEURE_DEBUT <
GD3.GDN_DATEHEURE_DEBUT
AND
GD4.GDN_DATEHEURE_FIN >=
GD3.GDN_DATEHEURE_DEBUT
)
)
-- élimination des sous périodes incluse dans les périodes de base
AND
NOT
EXISTS
(
SELECT
*
FROM
V_GDN GD5
WHERE
(
GD5.GDN_DATEHEURE_DEBUT <
GD1.GDN_DATEHEURE_DEBUT
AND
GD5.GDN_DATEHEURE_FIN >=
GD1.GDN_DATEHEURE_DEBUT )
OR
(
GD5.GDN_DATEHEURE_DEBUT <=
GD2.GDN_DATEHEURE_FIN
AND
GD5.GDN_DATEHEURE_FIN >
GD2.GDN_DATEHEURE_FIN )
)
)
-- il faut maintenant inverser le problème en trouvant les périodes ou il n'y a personne !
SELECT
T1.GDN_DATEHEURE_FIN AS
GDN_DATEHEURE_DEBUT,
MIN
(
T2.GDN_DATEHEURE_DEBUT)
AS
GDN_DATEHEURE_FIN
FROM
GDT T1
INNER
JOIN
GDT T2
ON
T2.GDN_DATEHEURE_DEBUT >
T1.GDN_DATEHEURE_FIN
GROUP
BY
T1.GDN_DATEHEURE_FIN
Comment cette requête fonctionne-t-elle ? Nous allons la décomposer étape par étape et comparer à chaque étape les lignes originelles avec celles calculées...
IV-I-2. Étape 1 : bornage des tranches sur la période à scruter▲
CREATE
VIEW
V_GDN
AS
SELECT
CASE
WHEN
GDN_DATEHEURE_DEBUT <
(
SELECT
MIN
(
DEBUT)
FROM
T_BORNE_BRN)
THEN
(
SELECT
MIN
(
DEBUT)
FROM
T_BORNE_BRN)
ELSE
GDN_DATEHEURE_DEBUT
END
AS
GDN_DATEHEURE_DEBUT,
CASE
WHEN
GDN_DATEHEURE_FIN >
(
SELECT
MAX
(
DEBUT)
FROM
T_BORNE_BRN)
THEN
(
SELECT
MAX
(
DEBUT)
FROM
T_BORNE_BRN)
ELSE
GDN_DATEHEURE_FIN
END
AS
GDN_DATEHEURE_FIN
FROM
dbo.T_GARDIENNAGE_GDN
UNION
ALL
SELECT
DEBUT, FIN
FROM
T_BORNE_BRN
-- requête de comparaison des tranches existantes avec les nouvelles tranches "recadrées" suite au bornage sur période à scruter
SELECT
*
FROM
(
SELECT
DISTINCT
T1.GDN_DATEHEURE_DEBUT AS
DEBUT_GARDIENNAGE,
T1.GDN_DATEHEURE_FIN AS
FIN_GARDIENNAGE,
T2.GDN_DATEHEURE_DEBUT AS
DEBUT_NOUVEAU,
T2.GDN_DATEHEURE_FIN AS
FIN_NOUVEAU
FROM
T_GARDIENNAGE_GDN T1
FULL
OUTER
JOIN
V_GDN T2
ON
T1.GDN_DATEHEURE_DEBUT =
T2.GDN_DATEHEURE_DEBUT
AND
T1.GDN_DATEHEURE_FIN =
T2.GDN_DATEHEURE_FIN
)
AS
T
ORDER
BY
COALESCE
(
DEBUT_GARDIENNAGE, DEBUT_NOUVEAU)
,
COALESCE
(
FIN_GARDIENNAGE, FIN_NOUVEAU)
DEBUT_GARDIENNAGE FIN_GARDIENNAGE DEBUT_NOUVEAU FIN_NOUVEAU
----------------- ---------------- ---------------- ----------------
NULL NULL 2008-09-01 00:00 2008-09-01 00:00 --> tranche ajoutée en début
2008-09-01 07:30 2008-09-01 14:30 2008-09-01 07:30 2008-09-01 14:30
2008-09-01 12:30 2008-09-02 00:00 2008-09-01 12:30 2008-09-02 00:00
2008-09-01 22:30 2008-09-02 08:00 2008-09-01 22:30 2008-09-02 08:00
2008-09-01 23:30 2008-09-02 11:00 2008-09-01 23:30 2008-09-02 11:00
2008-09-02 06:00 2008-09-02 17:30 2008-09-02 06:00 2008-09-02 17:30
2008-09-02 08:30 2008-09-02 16:00 2008-09-02 08:30 2008-09-02 16:00
2008-09-03 06:00 2008-09-03 17:30 2008-09-03 06:00 2008-09-03 17:30
2008-09-03 08:30 2008-09-03 16:00 2008-09-03 08:30 2008-09-03 16:00
2008-09-03 18:00 2008-09-04 04:30 2008-09-03 18:00 2008-09-04 04:30
2008-09-03 22:30 2008-09-04 10:00 2008-09-03 22:30 2008-09-04 10:00
2008-09-04 06:00 2008-09-04 17:30 2008-09-04 06:00 2008-09-04 17:30
2008-09-04 14:30 2008-09-05 08:30 2008-09-04 14:30 2008-09-05 08:30
2008-09-05 08:30 2008-09-05 21:30 2008-09-05 08:30 2008-09-05 21:30
2008-09-05 09:30 2008-09-05 12:00 2008-09-05 09:30 2008-09-05 12:00
2008-09-06 08:30 2008-09-06 13:30 2008-09-06 08:30 2008-09-06 13:30
2008-09-06 10:30 2008-09-06 21:00 2008-09-06 10:30 2008-09-06 21:00
2008-09-06 12:00 2008-09-06 22:30 2008-09-06 12:00 2008-09-06 22:30
2008-09-06 16:30 2008-09-07 10:30 2008-09-06 16:30 2008-09-07 10:30
2008-09-06 21:00 2008-09-07 10:30 2008-09-06 21:00 2008-09-07 10:30
2008-09-07 06:00 2008-09-07 12:30 2008-09-07 06:00 2008-09-07 12:30
2008-09-07 18:30 2008-09-08 06:30 2008-09-07 18:30 2008-09-07 23:59 --> tranche modifiée pour sa borne fin
NULL NULL 2008-09-07 23:59 2008-09-07 23:59 --> tranche ajoutée en fin
IV-I-3. Étape 2 : calcul de toutes les périodes possibles partant des périodes existantes▲
On met en relation toutes les fins avec tous les début à condition de respecter l'ordre chronologie que le début soit avant (ou égal : tranche de durée nulle) à la fin
CREATE
VIEW
GDT1
AS
-- on combine toutes les heures de début avec toutes les heures de fin qui sont supérieures à celle de début
-- on réalise en fait une matrice "triangulaire" (éclatement de périodes...)
SELECT
DISTINCT
GD1.GDN_DATEHEURE_DEBUT, GD2.GDN_DATEHEURE_FIN
FROM
V_GDN GD1
INNER
JOIN
V_GDN GD2
ON
GD1.GDN_DATEHEURE_DEBUT <=
GD2.GDN_DATEHEURE_FIN
SELECT
*
FROM
(
SELECT
DISTINCT
T1.GDN_DATEHEURE_DEBUT AS
DEBUT_GARDIENNAGE,
T1.GDN_DATEHEURE_FIN AS
FIN_GARDIENNAGE,
T2.GDN_DATEHEURE_DEBUT AS
DEBUT_NOUVEAU,
T2.GDN_DATEHEURE_FIN AS
FIN_NOUVEAU
FROM
T_GARDIENNAGE_GDN T1
FULL
OUTER
JOIN
GDT1 T2
ON
T1.GDN_DATEHEURE_DEBUT =
T2.GDN_DATEHEURE_DEBUT
AND
T1.GDN_DATEHEURE_FIN =
T2.GDN_DATEHEURE_FIN
)
AS
T
ORDER
BY
COALESCE
(
DEBUT_GARDIENNAGE, DEBUT_NOUVEAU)
,
COALESCE
(
FIN_GARDIENNAGE, FIN_NOUVEAU)
;
DEBUT_GARDIENNAGE FIN_GARDIENNAGE DEBUT_NOUVEAU FIN_NOUVEAU
----------------- ---------------- ---------------- ----------------
NULL NULL 2008-09-01 00:00 2008-09-01 00:00
NULL NULL 2008-09-01 00:00 2008-09-01 14:30
NULL NULL 2008-09-01 00:00 2008-09-02 00:00
NULL NULL 2008-09-01 00:00 2008-09-02 08:00
NULL NULL 2008-09-01 00:00 2008-09-02 11:00
NULL NULL 2008-09-01 00:00 2008-09-02 16:00
NULL NULL 2008-09-01 00:00 2008-09-02 17:30
NULL NULL 2008-09-01 00:00 2008-09-03 16:00
NULL NULL 2008-09-01 00:00 2008-09-03 17:30
NULL NULL 2008-09-01 00:00 2008-09-04 04:30
NULL NULL 2008-09-01 00:00 2008-09-04 10:00
NULL NULL 2008-09-01 00:00 2008-09-04 17:30
NULL NULL 2008-09-01 00:00 2008-09-05 08:30
NULL NULL 2008-09-01 00:00 2008-09-05 12:00
NULL NULL 2008-09-01 00:00 2008-09-05 21:30
NULL NULL 2008-09-01 00:00 2008-09-06 13:30
NULL NULL 2008-09-01 00:00 2008-09-06 21:00
NULL NULL 2008-09-01 00:00 2008-09-06 22:30
NULL NULL 2008-09-01 00:00 2008-09-07 10:30
NULL NULL 2008-09-01 00:00 2008-09-07 12:30
NULL NULL 2008-09-01 00:00 2008-09-07 23:59
2008-09-01 07:30 2008-09-01 14:30 2008-09-01 07:30 2008-09-01 14:30
NULL NULL 2008-09-01 07:30 2008-09-02 00:00
NULL NULL 2008-09-01 07:30 2008-09-02 08:00
NULL NULL 2008-09-01 07:30 2008-09-02 11:00
NULL NULL 2008-09-01 07:30 2008-09-02 16:00
NULL NULL 2008-09-01 07:30 2008-09-02 17:30
NULL NULL 2008-09-01 07:30 2008-09-03 16:00
NULL NULL 2008-09-01 07:30 2008-09-03 17:30
NULL NULL 2008-09-01 07:30 2008-09-04 04:30
NULL NULL 2008-09-01 07:30 2008-09-04 10:00
NULL NULL 2008-09-01 07:30 2008-09-04 17:30
NULL NULL 2008-09-01 07:30 2008-09-05 08:30
NULL NULL 2008-09-01 07:30 2008-09-05 12:00
NULL NULL 2008-09-01 07:30 2008-09-05 21:30
NULL NULL 2008-09-01 07:30 2008-09-06 13:30
NULL NULL 2008-09-01 07:30 2008-09-06 21:00
NULL NULL 2008-09-01 07:30 2008-09-06 22:30
NULL NULL 2008-09-01 07:30 2008-09-07 10:30
NULL NULL 2008-09-01 07:30 2008-09-07 12:30
NULL NULL 2008-09-01 07:30 2008-09-07 23:59
NULL NULL 2008-09-01 12:30 2008-09-01 14:30
2008-09-01 12:30 2008-09-02 00:00 2008-09-01 12:30 2008-09-02 00:00
NULL NULL 2008-09-01 12:30 2008-09-02 08:00
NULL NULL 2008-09-01 12:30 2008-09-02 11:00
NULL NULL 2008-09-01 12:30 2008-09-02 16:00
NULL NULL 2008-09-01 12:30 2008-09-02 17:30
NULL NULL 2008-09-01 12:30 2008-09-03 16:00
NULL NULL 2008-09-01 12:30 2008-09-03 17:30
NULL NULL 2008-09-01 12:30 2008-09-04 04:30
NULL NULL 2008-09-01 12:30 2008-09-04 10:00
NULL NULL 2008-09-01 12:30 2008-09-04 17:30
NULL NULL 2008-09-01 12:30 2008-09-05 08:30
NULL NULL 2008-09-01 12:30 2008-09-05 12:00
NULL NULL 2008-09-01 12:30 2008-09-05 21:30
NULL NULL 2008-09-01 12:30 2008-09-06 13:30
NULL NULL 2008-09-01 12:30 2008-09-06 21:00
NULL NULL 2008-09-01 12:30 2008-09-06 22:30
NULL NULL 2008-09-01 12:30 2008-09-07 10:30
NULL NULL 2008-09-01 12:30 2008-09-07 12:30
NULL NULL 2008-09-01 12:30 2008-09-07 23:59
NULL NULL 2008-09-01 22:30 2008-09-02 00:00
2008-09-01 22:30 2008-09-02 08:00 2008-09-01 22:30 2008-09-02 08:00
NULL NULL 2008-09-01 22:30 2008-09-02 11:00
NULL NULL 2008-09-01 22:30 2008-09-02 16:00
NULL NULL 2008-09-01 22:30 2008-09-02 17:30
NULL NULL 2008-09-01 22:30 2008-09-03 16:00
NULL NULL 2008-09-01 22:30 2008-09-03 17:30
NULL NULL 2008-09-01 22:30 2008-09-04 04:30
NULL NULL 2008-09-01 22:30 2008-09-04 10:00
NULL NULL 2008-09-01 22:30 2008-09-04 17:30
NULL NULL 2008-09-01 22:30 2008-09-05 08:30
NULL NULL 2008-09-01 22:30 2008-09-05 12:00
NULL NULL 2008-09-01 22:30 2008-09-05 21:30
NULL NULL 2008-09-01 22:30 2008-09-06 13:30
NULL NULL 2008-09-01 22:30 2008-09-06 21:00
NULL NULL 2008-09-01 22:30 2008-09-06 22:30
NULL NULL 2008-09-01 22:30 2008-09-07 10:30
NULL NULL 2008-09-01 22:30 2008-09-07 12:30
NULL NULL 2008-09-01 22:30 2008-09-07 23:59
NULL NULL 2008-09-01 23:30 2008-09-02 00:00
NULL NULL 2008-09-01 23:30 2008-09-02 08:00
2008-09-01 23:30 2008-09-02 11:00 2008-09-01 23:30 2008-09-02 11:00
NULL NULL 2008-09-01 23:30 2008-09-02 16:00
NULL NULL 2008-09-01 23:30 2008-09-02 17:30
NULL NULL 2008-09-01 23:30 2008-09-03 16:00
NULL NULL 2008-09-01 23:30 2008-09-03 17:30
NULL NULL 2008-09-01 23:30 2008-09-04 04:30
NULL NULL 2008-09-01 23:30 2008-09-04 10:00
NULL NULL 2008-09-01 23:30 2008-09-04 17:30
NULL NULL 2008-09-01 23:30 2008-09-05 08:30
NULL NULL 2008-09-01 23:30 2008-09-05 12:00
NULL NULL 2008-09-01 23:30 2008-09-05 21:30
NULL NULL 2008-09-01 23:30 2008-09-06 13:30
NULL NULL 2008-09-01 23:30 2008-09-06 21:00
NULL NULL 2008-09-01 23:30 2008-09-06 22:30
NULL NULL 2008-09-01 23:30 2008-09-07 10:30
NULL NULL 2008-09-01 23:30 2008-09-07 12:30
NULL NULL 2008-09-01 23:30 2008-09-07 23:59
NULL NULL 2008-09-02 06:00 2008-09-02 08:00
NULL NULL 2008-09-02 06:00 2008-09-02 11:00
NULL NULL 2008-09-02 06:00 2008-09-02 16:00
2008-09-02 06:00 2008-09-02 17:30 2008-09-02 06:00 2008-09-02 17:30
NULL NULL 2008-09-02 06:00 2008-09-03 16:00
NULL NULL 2008-09-02 06:00 2008-09-03 17:30
NULL NULL 2008-09-02 06:00 2008-09-04 04:30
NULL NULL 2008-09-02 06:00 2008-09-04 10:00
NULL NULL 2008-09-02 06:00 2008-09-04 17:30
NULL NULL 2008-09-02 06:00 2008-09-05 08:30
NULL NULL 2008-09-02 06:00 2008-09-05 12:00
NULL NULL 2008-09-02 06:00 2008-09-05 21:30
NULL NULL 2008-09-02 06:00 2008-09-06 13:30
NULL NULL 2008-09-02 06:00 2008-09-06 21:00
NULL NULL 2008-09-02 06:00 2008-09-06 22:30
NULL NULL 2008-09-02 06:00 2008-09-07 10:30
NULL NULL 2008-09-02 06:00 2008-09-07 12:30
NULL NULL 2008-09-02 06:00 2008-09-07 23:59
NULL NULL 2008-09-02 08:30 2008-09-02 11:00
2008-09-02 08:30 2008-09-02 16:00 2008-09-02 08:30 2008-09-02 16:00
NULL NULL 2008-09-02 08:30 2008-09-02 17:30
NULL NULL 2008-09-02 08:30 2008-09-03 16:00
NULL NULL 2008-09-02 08:30 2008-09-03 17:30
NULL NULL 2008-09-02 08:30 2008-09-04 04:30
NULL NULL 2008-09-02 08:30 2008-09-04 10:00
NULL NULL 2008-09-02 08:30 2008-09-04 17:30
NULL NULL 2008-09-02 08:30 2008-09-05 08:30
NULL NULL 2008-09-02 08:30 2008-09-05 12:00
NULL NULL 2008-09-02 08:30 2008-09-05 21:30
NULL NULL 2008-09-02 08:30 2008-09-06 13:30
NULL NULL 2008-09-02 08:30 2008-09-06 21:00
NULL NULL 2008-09-02 08:30 2008-09-06 22:30
NULL NULL 2008-09-02 08:30 2008-09-07 10:30
NULL NULL 2008-09-02 08:30 2008-09-07 12:30
NULL NULL 2008-09-02 08:30 2008-09-07 23:59
NULL NULL 2008-09-03 06:00 2008-09-03 16:00
2008-09-03 06:00 2008-09-03 17:30 2008-09-03 06:00 2008-09-03 17:30
NULL NULL 2008-09-03 06:00 2008-09-04 04:30
NULL NULL 2008-09-03 06:00 2008-09-04 10:00
NULL NULL 2008-09-03 06:00 2008-09-04 17:30
NULL NULL 2008-09-03 06:00 2008-09-05 08:30
NULL NULL 2008-09-03 06:00 2008-09-05 12:00
NULL NULL 2008-09-03 06:00 2008-09-05 21:30
NULL NULL 2008-09-03 06:00 2008-09-06 13:30
NULL NULL 2008-09-03 06:00 2008-09-06 21:00
NULL NULL 2008-09-03 06:00 2008-09-06 22:30
NULL NULL 2008-09-03 06:00 2008-09-07 10:30
NULL NULL 2008-09-03 06:00 2008-09-07 12:30
NULL NULL 2008-09-03 06:00 2008-09-07 23:59
2008-09-03 08:30 2008-09-03 16:00 2008-09-03 08:30 2008-09-03 16:00
NULL NULL 2008-09-03 08:30 2008-09-03 17:30
NULL NULL 2008-09-03 08:30 2008-09-04 04:30
NULL NULL 2008-09-03 08:30 2008-09-04 10:00
NULL NULL 2008-09-03 08:30 2008-09-04 17:30
NULL NULL 2008-09-03 08:30 2008-09-05 08:30
NULL NULL 2008-09-03 08:30 2008-09-05 12:00
NULL NULL 2008-09-03 08:30 2008-09-05 21:30
NULL NULL 2008-09-03 08:30 2008-09-06 13:30
NULL NULL 2008-09-03 08:30 2008-09-06 21:00
NULL NULL 2008-09-03 08:30 2008-09-06 22:30
NULL NULL 2008-09-03 08:30 2008-09-07 10:30
NULL NULL 2008-09-03 08:30 2008-09-07 12:30
NULL NULL 2008-09-03 08:30 2008-09-07 23:59
2008-09-03 18:00 2008-09-04 04:30 2008-09-03 18:00 2008-09-04 04:30
NULL NULL 2008-09-03 18:00 2008-09-04 10:00
NULL NULL 2008-09-03 18:00 2008-09-04 17:30
NULL NULL 2008-09-03 18:00 2008-09-05 08:30
NULL NULL 2008-09-03 18:00 2008-09-05 12:00
NULL NULL 2008-09-03 18:00 2008-09-05 21:30
NULL NULL 2008-09-03 18:00 2008-09-06 13:30
NULL NULL 2008-09-03 18:00 2008-09-06 21:00
NULL NULL 2008-09-03 18:00 2008-09-06 22:30
NULL NULL 2008-09-03 18:00 2008-09-07 10:30
NULL NULL 2008-09-03 18:00 2008-09-07 12:30
NULL NULL 2008-09-03 18:00 2008-09-07 23:59
NULL NULL 2008-09-03 22:30 2008-09-04 04:30
2008-09-03 22:30 2008-09-04 10:00 2008-09-03 22:30 2008-09-04 10:00
NULL NULL 2008-09-03 22:30 2008-09-04 17:30
NULL NULL 2008-09-03 22:30 2008-09-05 08:30
NULL NULL 2008-09-03 22:30 2008-09-05 12:00
NULL NULL 2008-09-03 22:30 2008-09-05 21:30
NULL NULL 2008-09-03 22:30 2008-09-06 13:30
NULL NULL 2008-09-03 22:30 2008-09-06 21:00
NULL NULL 2008-09-03 22:30 2008-09-06 22:30
NULL NULL 2008-09-03 22:30 2008-09-07 10:30
NULL NULL 2008-09-03 22:30 2008-09-07 12:30
NULL NULL 2008-09-03 22:30 2008-09-07 23:59
NULL NULL 2008-09-04 06:00 2008-09-04 10:00
2008-09-04 06:00 2008-09-04 17:30 2008-09-04 06:00 2008-09-04 17:30
NULL NULL 2008-09-04 06:00 2008-09-05 08:30
NULL NULL 2008-09-04 06:00 2008-09-05 12:00
NULL NULL 2008-09-04 06:00 2008-09-05 21:30
NULL NULL 2008-09-04 06:00 2008-09-06 13:30
NULL NULL 2008-09-04 06:00 2008-09-06 21:00
NULL NULL 2008-09-04 06:00 2008-09-06 22:30
NULL NULL 2008-09-04 06:00 2008-09-07 10:30
NULL NULL 2008-09-04 06:00 2008-09-07 12:30
NULL NULL 2008-09-04 06:00 2008-09-07 23:59
NULL NULL 2008-09-04 14:30 2008-09-04 17:30
2008-09-04 14:30 2008-09-05 08:30 2008-09-04 14:30 2008-09-05 08:30
NULL NULL 2008-09-04 14:30 2008-09-05 12:00
NULL NULL 2008-09-04 14:30 2008-09-05 21:30
NULL NULL 2008-09-04 14:30 2008-09-06 13:30
NULL NULL 2008-09-04 14:30 2008-09-06 21:00
NULL NULL 2008-09-04 14:30 2008-09-06 22:30
NULL NULL 2008-09-04 14:30 2008-09-07 10:30
NULL NULL 2008-09-04 14:30 2008-09-07 12:30
NULL NULL 2008-09-04 14:30 2008-09-07 23:59
NULL NULL 2008-09-05 08:30 2008-09-05 08:30
NULL NULL 2008-09-05 08:30 2008-09-05 12:00
2008-09-05 08:30 2008-09-05 21:30 2008-09-05 08:30 2008-09-05 21:30
NULL NULL 2008-09-05 08:30 2008-09-06 13:30
NULL NULL 2008-09-05 08:30 2008-09-06 21:00
NULL NULL 2008-09-05 08:30 2008-09-06 22:30
NULL NULL 2008-09-05 08:30 2008-09-07 10:30
NULL NULL 2008-09-05 08:30 2008-09-07 12:30
NULL NULL 2008-09-05 08:30 2008-09-07 23:59
2008-09-05 09:30 2008-09-05 12:00 2008-09-05 09:30 2008-09-05 12:00
NULL NULL 2008-09-05 09:30 2008-09-05 21:30
NULL NULL 2008-09-05 09:30 2008-09-06 13:30
NULL NULL 2008-09-05 09:30 2008-09-06 21:00
NULL NULL 2008-09-05 09:30 2008-09-06 22:30
NULL NULL 2008-09-05 09:30 2008-09-07 10:30
NULL NULL 2008-09-05 09:30 2008-09-07 12:30
NULL NULL 2008-09-05 09:30 2008-09-07 23:59
2008-09-06 08:30 2008-09-06 13:30 2008-09-06 08:30 2008-09-06 13:30
NULL NULL 2008-09-06 08:30 2008-09-06 21:00
NULL NULL 2008-09-06 08:30 2008-09-06 22:30
NULL NULL 2008-09-06 08:30 2008-09-07 10:30
NULL NULL 2008-09-06 08:30 2008-09-07 12:30
NULL NULL 2008-09-06 08:30 2008-09-07 23:59
NULL NULL 2008-09-06 10:30 2008-09-06 13:30
2008-09-06 10:30 2008-09-06 21:00 2008-09-06 10:30 2008-09-06 21:00
NULL NULL 2008-09-06 10:30 2008-09-06 22:30
NULL NULL 2008-09-06 10:30 2008-09-07 10:30
NULL NULL 2008-09-06 10:30 2008-09-07 12:30
NULL NULL 2008-09-06 10:30 2008-09-07 23:59
NULL NULL 2008-09-06 12:00 2008-09-06 13:30
NULL NULL 2008-09-06 12:00 2008-09-06 21:00
2008-09-06 12:00 2008-09-06 22:30 2008-09-06 12:00 2008-09-06 22:30
NULL NULL 2008-09-06 12:00 2008-09-07 10:30
NULL NULL 2008-09-06 12:00 2008-09-07 12:30
NULL NULL 2008-09-06 12:00 2008-09-07 23:59
NULL NULL 2008-09-06 16:30 2008-09-06 21:00
NULL NULL 2008-09-06 16:30 2008-09-06 22:30
2008-09-06 16:30 2008-09-07 10:30 2008-09-06 16:30 2008-09-07 10:30
NULL NULL 2008-09-06 16:30 2008-09-07 12:30
NULL NULL 2008-09-06 16:30 2008-09-07 23:59
NULL NULL 2008-09-06 21:00 2008-09-06 21:00
NULL NULL 2008-09-06 21:00 2008-09-06 22:30
2008-09-06 21:00 2008-09-07 10:30 2008-09-06 21:00 2008-09-07 10:30
NULL NULL 2008-09-06 21:00 2008-09-07 12:30
NULL NULL 2008-09-06 21:00 2008-09-07 23:59
NULL NULL 2008-09-07 06:00 2008-09-07 10:30
2008-09-07 06:00 2008-09-07 12:30 2008-09-07 06:00 2008-09-07 12:30
NULL NULL 2008-09-07 06:00 2008-09-07 23:59
NULL NULL 2008-09-07 18:30 2008-09-07 23:59
2008-09-07 18:30 2008-09-08 06:30 NULL NULL
NULL NULL 2008-09-07 23:59 2008-09-07 23:59
38-D. Étape 3 : on supprime maintenant les périodes incluses dans les autres afin de ne laisser que des périodes non "redondantes"▲
CREATE
VIEW
GDT2
AS
-- on combine toutes les heures de début avec toutes les heures de fin qui sont supérieures à celle de début
-- on réalise en fait une matrice "triangulaire"
SELECT
DISTINCT
GD1.GDN_DATEHEURE_DEBUT, GD2.GDN_DATEHEURE_FIN
FROM
V_GDN GD1
INNER
JOIN
V_GDN GD2
ON
GD1.GDN_DATEHEURE_DEBUT <=
GD2.GDN_DATEHEURE_FIN
-- élimination des sous périodes incluse dans les périodes de base
AND
NOT
EXISTS
(
SELECT
*
FROM
V_GDN GD5
WHERE
(
GD5.GDN_DATEHEURE_DEBUT <
GD1.GDN_DATEHEURE_DEBUT
AND
GD5.GDN_DATEHEURE_FIN >=
GD1.GDN_DATEHEURE_DEBUT )
OR
(
GD5.GDN_DATEHEURE_DEBUT <=
GD2.GDN_DATEHEURE_FIN
AND
GD5.GDN_DATEHEURE_FIN >
GD2.GDN_DATEHEURE_FIN )
)
;
SELECT
*
FROM
(
SELECT
DISTINCT
T1.GDN_DATEHEURE_DEBUT AS
DEBUT_GARDIENNAGE,
T1.GDN_DATEHEURE_FIN AS
FIN_GARDIENNAGE,
T2.GDN_DATEHEURE_DEBUT AS
DEBUT_NOUVEAU,
T2.GDN_DATEHEURE_FIN AS
FIN_NOUVEAU
FROM
T_GARDIENNAGE_GDN T1
FULL
OUTER
JOIN
GDT2 T2
ON
T1.GDN_DATEHEURE_DEBUT =
T2.GDN_DATEHEURE_DEBUT
AND
T1.GDN_DATEHEURE_FIN =
T2.GDN_DATEHEURE_FIN
)
AS
T
ORDER
BY
COALESCE
(
DEBUT_GARDIENNAGE, DEBUT_NOUVEAU)
,
COALESCE
(
FIN_GARDIENNAGE, FIN_NOUVEAU)
DEBUT_GARDIENNAGE FIN_GARDIENNAGE DEBUT_NOUVEAU FIN_NOUVEAU
----------------- ---------------- ---------------- ----------------
NULL NULL 2008-09-01 00:00 2008-09-01 00:00
NULL NULL 2008-09-01 00:00 2008-09-02 17:30
NULL NULL 2008-09-01 00:00 2008-09-03 17:30
NULL NULL 2008-09-01 00:00 2008-09-05 21:30
NULL NULL 2008-09-01 00:00 2008-09-07 12:30
NULL NULL 2008-09-01 00:00 2008-09-07 23:59
2008-09-01 07:30 2008-09-01 14:30 NULL NULL
NULL NULL 2008-09-01 07:30 2008-09-02 17:30
NULL NULL 2008-09-01 07:30 2008-09-03 17:30
NULL NULL 2008-09-01 07:30 2008-09-05 21:30
NULL NULL 2008-09-01 07:30 2008-09-07 12:30
NULL NULL 2008-09-01 07:30 2008-09-07 23:59
2008-09-01 12:30 2008-09-02 00:00 NULL NULL
2008-09-01 22:30 2008-09-02 08:00 NULL NULL
2008-09-01 23:30 2008-09-02 11:00 NULL NULL
2008-09-02 06:00 2008-09-02 17:30 NULL NULL
2008-09-02 08:30 2008-09-02 16:00 NULL NULL
2008-09-03 06:00 2008-09-03 17:30 2008-09-03 06:00 2008-09-03 17:30
NULL NULL 2008-09-03 06:00 2008-09-05 21:30
NULL NULL 2008-09-03 06:00 2008-09-07 12:30
NULL NULL 2008-09-03 06:00 2008-09-07 23:59
2008-09-03 08:30 2008-09-03 16:00 NULL NULL
2008-09-03 18:00 2008-09-04 04:30 NULL NULL
NULL NULL 2008-09-03 18:00 2008-09-05 21:30
NULL NULL 2008-09-03 18:00 2008-09-07 12:30
NULL NULL 2008-09-03 18:00 2008-09-07 23:59
2008-09-03 22:30 2008-09-04 10:00 NULL NULL
2008-09-04 06:00 2008-09-04 17:30 NULL NULL
2008-09-04 14:30 2008-09-05 08:30 NULL NULL
2008-09-05 08:30 2008-09-05 21:30 NULL NULL
2008-09-05 09:30 2008-09-05 12:00 NULL NULL
2008-09-06 08:30 2008-09-06 13:30 NULL NULL
NULL NULL 2008-09-06 08:30 2008-09-07 12:30
NULL NULL 2008-09-06 08:30 2008-09-07 23:59
2008-09-06 10:30 2008-09-06 21:00 NULL NULL
2008-09-06 12:00 2008-09-06 22:30 NULL NULL
2008-09-06 16:30 2008-09-07 10:30 NULL NULL
2008-09-06 21:00 2008-09-07 10:30 NULL NULL
2008-09-07 06:00 2008-09-07 12:30 NULL NULL
NULL NULL 2008-09-07 18:30 2008-09-07 23:59
2008-09-07 18:30 2008-09-08 06:30 NULL NULL
38-E. Étape 4 : en sus de de débarrasser de la redondance, on se débarrase des tranches faisant des "trous" au sein d'une même période▲
CREATE
VIEW
GDT3
AS
-- on combine toutes les heures de début avec toutes les heures de fin qui sont supérieures à celle de début
-- on réalise en fait une matrice "triangulaire"
SELECT
DISTINCT
GD1.GDN_DATEHEURE_DEBUT, GD2.GDN_DATEHEURE_FIN
FROM
V_GDN GD1
INNER
JOIN
V_GDN GD2
ON
GD1.GDN_DATEHEURE_DEBUT <=
GD2.GDN_DATEHEURE_FIN
-- élimination des sous périodes incluse dans les périodes de base
AND
NOT
EXISTS
(
SELECT
*
FROM
V_GDN GD5
WHERE
(
GD5.GDN_DATEHEURE_DEBUT <
GD1.GDN_DATEHEURE_DEBUT
AND
GD5.GDN_DATEHEURE_FIN >=
GD1.GDN_DATEHEURE_DEBUT )
OR
(
GD5.GDN_DATEHEURE_DEBUT <=
GD2.GDN_DATEHEURE_FIN
AND
GD5.GDN_DATEHEURE_FIN >
GD2.GDN_DATEHEURE_FIN )
)
-- élimination des trous entre GD1.GDN_DATEHEURE_DEBUT et GD2.GDN_DATEHEURE_FIN
AND
NOT
EXISTS
(
SELECT
*
FROM
V_GDN GD3
WHERE
GD3.GDN_DATEHEURE_DEBUT >
GD1.GDN_DATEHEURE_FIN
AND
GD3.GDN_DATEHEURE_DEBUT <=
GD2.GDN_DATEHEURE_DEBUT
AND
NOT
EXISTS
(
SELECT
*
FROM
V_GDN GD4
WHERE
GD4.GDN_DATEHEURE_DEBUT <
GD3.GDN_DATEHEURE_DEBUT
AND
GD4.GDN_DATEHEURE_FIN >=
GD3.GDN_DATEHEURE_DEBUT
)
)
SELECT
*
FROM
(
SELECT
DISTINCT
T1.GDN_DATEHEURE_DEBUT AS
DEBUT_GARDIENNAGE,
T1.GDN_DATEHEURE_FIN AS
FIN_GARDIENNAGE,
T2.GDN_DATEHEURE_DEBUT AS
DEBUT_NOUVEAU,
T2.GDN_DATEHEURE_FIN AS
FIN_NOUVEAU
FROM
T_GARDIENNAGE_GDN T1
FULL
OUTER
JOIN
GDT3 T2
ON
T1.GDN_DATEHEURE_DEBUT =
T2.GDN_DATEHEURE_DEBUT
AND
T1.GDN_DATEHEURE_FIN =
T2.GDN_DATEHEURE_FIN
)
AS
T
ORDER
BY
COALESCE
(
DEBUT_GARDIENNAGE, DEBUT_NOUVEAU)
,
COALESCE
(
FIN_GARDIENNAGE, FIN_NOUVEAU)
DEBUT_GARDIENNAGE FIN_GARDIENNAGE DEBUT_NOUVEAU FIN_NOUVEAU
----------------- ---------------- ---------------- ----------------
NULL NULL 2008-09-01 00:00 2008-09-01 00:00
2008-09-01 07:30 2008-09-01 14:30 NULL NULL
NULL NULL 2008-09-01 07:30 2008-09-02 17:30
2008-09-01 12:30 2008-09-02 00:00 NULL NULL
2008-09-01 22:30 2008-09-02 08:00 NULL NULL
2008-09-01 23:30 2008-09-02 11:00 NULL NULL
2008-09-02 06:00 2008-09-02 17:30 NULL NULL
2008-09-02 08:30 2008-09-02 16:00 NULL NULL
2008-09-03 06:00 2008-09-03 17:30 2008-09-03 06:00 2008-09-03 17:30
2008-09-03 08:30 2008-09-03 16:00 NULL NULL
2008-09-03 18:00 2008-09-04 04:30 NULL NULL
NULL NULL 2008-09-03 18:00 2008-09-05 21:30
2008-09-03 22:30 2008-09-04 10:00 NULL NULL
2008-09-04 06:00 2008-09-04 17:30 NULL NULL
2008-09-04 14:30 2008-09-05 08:30 NULL NULL
2008-09-05 08:30 2008-09-05 21:30 NULL NULL
2008-09-05 09:30 2008-09-05 12:00 NULL NULL
2008-09-06 08:30 2008-09-06 13:30 NULL NULL
NULL NULL 2008-09-06 08:30 2008-09-07 12:30
2008-09-06 10:30 2008-09-06 21:00 NULL NULL
2008-09-06 12:00 2008-09-06 22:30 NULL NULL
2008-09-06 16:30 2008-09-07 10:30 NULL NULL
2008-09-06 21:00 2008-09-07 10:30 NULL NULL
2008-09-07 06:00 2008-09-07 12:30 NULL NULL
NULL NULL 2008-09-07 18:30 2008-09-07 23:59
2008-09-07 18:30 2008-09-08 06:30 NULL NULL
Notez que les étapes 3 et 4 sont réalisées dans la même CTE.
38-F. Étape 5 (finale): recherche des intervalles inverses▲
Il faut maintenant inverser le problème en trouvant les périodes où il n'y a personne !
CREATE
VIEW
GDT4
AS
SELECT
T1.GDN_DATEHEURE_FIN AS
GDN_DATEHEURE_DEBUT,
MIN
(
T2.GDN_DATEHEURE_DEBUT)
AS
GDN_DATEHEURE_FIN
FROM
GDT3 T1
INNER
JOIN
GDT3 T2
ON
T2.GDN_DATEHEURE_DEBUT >
T1.GDN_DATEHEURE_FIN
GROUP
BY
T1.GDN_DATEHEURE_FIN;
Comparaison des tranches existantes avec les tranches vides enfin calculées :
SELECT
*
FROM
(
SELECT
DISTINCT
T1.GDN_DATEHEURE_DEBUT AS
DEBUT_GARDIENNAGE,
T1.GDN_DATEHEURE_FIN AS
FIN_GARDIENNAGE,
T2.GDN_DATEHEURE_DEBUT AS
DEBUT_NOUVEAU,
T2.GDN_DATEHEURE_FIN AS
FIN_NOUVEAU
FROM
T_GARDIENNAGE_GDN T1
FULL
OUTER
JOIN
GDT4 T2
ON
T1.GDN_DATEHEURE_DEBUT =
T2.GDN_DATEHEURE_DEBUT
AND
T1.GDN_DATEHEURE_FIN =
T2.GDN_DATEHEURE_FIN
)
AS
T
ORDER
BY
COALESCE
(
DEBUT_GARDIENNAGE, DEBUT_NOUVEAU)
,
COALESCE
(
FIN_GARDIENNAGE, FIN_NOUVEAU)
;
DEBUT_GARDIENNAGE FIN_GARDIENNAGE DEBUT_NOUVEAU FIN_NOUVEAU
----------------- ---------------- ---------------- ----------------
NULL NULL 2008-09-01 00:00 2008-09-01 07:30
2008-09-01 07:30 2008-09-01 14:30 NULL NULL
2008-09-01 12:30 2008-09-02 00:00 NULL NULL
2008-09-01 22:30 2008-09-02 08:00 NULL NULL
2008-09-01 23:30 2008-09-02 11:00 NULL NULL
2008-09-02 06:00 2008-09-02 17:30 NULL NULL
2008-09-02 08:30 2008-09-02 16:00 NULL NULL
NULL NULL 2008-09-02 17:30 2008-09-03 06:00
2008-09-03 06:00 2008-09-03 17:30 NULL NULL
2008-09-03 08:30 2008-09-03 16:00 NULL NULL
NULL NULL 2008-09-03 17:30 2008-09-03 18:00
2008-09-03 18:00 2008-09-04 04:30 NULL NULL
2008-09-03 22:30 2008-09-04 10:00 NULL NULL
2008-09-04 06:00 2008-09-04 17:30 NULL NULL
2008-09-04 14:30 2008-09-05 08:30 NULL NULL
2008-09-05 08:30 2008-09-05 21:30 NULL NULL
2008-09-05 09:30 2008-09-05 12:00 NULL NULL
NULL NULL 2008-09-05 21:30 2008-09-06 08:30
2008-09-06 08:30 2008-09-06 13:30 NULL NULL
2008-09-06 10:30 2008-09-06 21:00 NULL NULL
2008-09-06 12:00 2008-09-06 22:30 NULL NULL
2008-09-06 16:30 2008-09-07 10:30 NULL NULL
2008-09-06 21:00 2008-09-07 10:30 NULL NULL
2008-09-07 06:00 2008-09-07 12:30 NULL NULL
NULL NULL 2008-09-07 12:30 2008-09-07 18:30
2008-09-07 18:30 2008-09-08 06:30 NULL NULL
39. Solution au problème n° 39 - abstract et mots clefs▲
On comprend bien que pour réaliser le comptage il faut que l'on arrive à une table telle que chaque mot ait été découpé et mis dans une colonne avec la référence du titre du papier.
La difficulté de ce découpage réside en deux problèmes :
39-A. Le découpage n'est pas itératif▲
En effet, si les données étaient telles que l'on aurait autant de caractère dièse (#) que de mot, la découpe serait simplifiée par le fait que trouver le début ou la fin du mot clef, consisterait à trouver la position de chaque caractères #. Or ce n'est pas le cas. Il convient donc d'agir en conséquence et faire deux traitements distincts.
Par exemple, trouver le premier mot clef de l'abstract revient à faire la requête suivante :
SELECT
*
, CASE
WHEN
POSITION
(
'#'
, PPR_ABSTRACT)
>
0
THEN
SUBSTRING
(
PPR_ABSTRACT FROM
1
FOR
COALESCE
(
POSITION
(
'#'
, PPR_ABSTRACT)
-
1
, 0
))
ELSE
PPR_ABSTRACT
END
AS
MOT_CLEF
FROM
T_PAPIER_PPR T ;
PPR_TITRE PPR_ABSTRACT MOT_CLEF1
------------------------------------ ------------------------------------ -------------
Les derniers roi de France Histoire#Politique Histoire
De Gaulle, un héro de l'histoire Histoire#Politique#Guerre Histoire
Les deux guerres mondiales Histoire#Guerre Histoire
Les années 50 en Europe Histoire#Union européenne Histoire
Les présidents de la république Histoire#Politique Histoire
Histoire de France Histoire Histoire
39-B. Le découpage n'est pas "fini"▲
En effet, le nombre de mots clef dans l'abstract n'est pas limité. Ici dans notre exemple nous avons fait en sorte de ne pas avoir une colonne PPR_ABSTRACT dont la longueur dépasse 120 caractères, mais dans la réalité, les choses ne sont pas bornées. Il faut donc prévoir un découpage de l'abstract sur un nombre indéterminé de mots clefs.
La solution à ce problème consiste à passer par une table de nombre et de faire un produit cartésien pour assurer le découpage des mots clefs...
Créons comme à notre habitude une table des nombres pour ce faire :
CREATE
TABLE
T_NUM (
NUM INTEGER
)
;
INSERT
INTO
T_NUM VALUES
(
0
)
;
INSERT
INTO
T_NUM VALUES
(
1
)
;
INSERT
INTO
T_NUM VALUES
(
2
)
;
INSERT
INTO
T_NUM VALUES
(
3
)
;
INSERT
INTO
T_NUM VALUES
(
4
)
;
INSERT
INTO
T_NUM VALUES
(
5
)
;
INSERT
INTO
T_NUM VALUES
(
6
)
;
INSERT
INTO
T_NUM VALUES
(
7
)
;
INSERT
INTO
T_NUM VALUES
(
8
)
;
INSERT
INTO
T_NUM VALUES
(
9
)
;
INSERT
INTO
T_NUM
SELECT
T1.NUM +
10
*
T2.NUM +
100
*
T3.NUM
FROM
T_NUM AS
T1
CROSS
JOIN
T_NUM AS
T2
CROSS
JOIN
T_NUM AS
T3
WHERE
T1.NUM +
10
*
T2.NUM +
100
*
T3.NUM >
9
;
Une voie pour parvenir à ce résultat est la requête suivante :
SELECT
PPR_TITRE, PPR_ABSTRACT,
SUBSTRING
(
PPR_ABSTRACT, N.NUM +
1
, LEN(
PPR_ABSTRACT)
-
N.NUM +
1
)
AS
DECOUPE
FROM
T_PAPIER_PPR T
CROSS
JOIN
T_NUM N
WHERE
SUBSTRING
(
PPR_ABSTRACT, N.NUM, 1
)
=
'#'
;
PPR_TITRE PPR_ABSTRACT DECOUPE
------------------------------------ ---------------------------- --------------------------------------------
Les derniers roi de France Histoire#Politique Politique
De Gaulle, un héro de l'histoire Histoire#Politique#Guerre Politique#Guerre
De Gaulle, un héro de l'histoire Histoire#Politique#Guerre Guerre
Les deux guerres mondiales Histoire#Guerre Guerre
Les années 50 en Europe Histoire#Union européenne Union européenne
Les présidents de la république Histoire#Politique Politique
Il ne suffit plus que de nettoyer cette table en ôtant tout ce qui est situé après le caractères # dans la colonne DECOUPE de faire une union avec la précédente requête et enfin de compter les occurrences.
Voici donc la requête finale :
SELECT
CAST
(
MOT_CLEF AS
VARCHAR
(
16
))
AS
MOT_CLEF, COUNT
(*)
AS
NBR_REPONSES
FROM
(
SELECT
PPR_TITRE, PPR_ABSTRACT, SUBSTRING
(
MOT_CLEF, 1
,
COALESCE
(
NULLIF
(
CHARINDEX(
'#'
, MOT_CLEF)
, 0
)
-
1
, LEN(
MOT_CLEF)))
AS
MOT_CLEF
FROM
(
SELECT
*
, CASE
WHEN
CHARINDEX(
'#'
, PPR_ABSTRACT)
>
0
THEN
SUBSTRING
(
PPR_ABSTRACT, 1
, COALESCE
(
CHARINDEX(
'#'
, PPR_ABSTRACT)
-
1
, 0
))
ELSE
PPR_ABSTRACT
END
AS
MOT_CLEF
FROM
T_PAPIER_PPR T
UNION
SELECT
PPR_TITRE, PPR_ABSTRACT,
SUBSTRING
(
PPR_ABSTRACT, N.NUM +
1
, LEN(
PPR_ABSTRACT)
-
N.NUM +
1
)
AS
DECOUPE
FROM
T_PAPIER_PPR T
CROSS
JOIN
T_NUM N
WHERE
SUBSTRING
(
PPR_ABSTRACT, N.NUM, 1
)
=
'#'
)
T)
TT
GROUP
BY
MOT_CLEF ;
Une autre façon de faire est de rajouter un # en tête de l'abstract dès le
départ :
SELECT
PPR_TITRE, '#'
+
PPR_ABSTRACT AS
PPR_ABSTRACT
FROM
T_PAPIER_PPR
PPR_TITRE PPR_ABSTRACT
------------------------------------ -------------------------------
Les derniers roi de France #Histoire#Politique
De Gaulle, un héro de l'histoire #Histoire#Politique#Guerre
Les deux guerres mondiales #Histoire#Guerre
Les années 50 en Europe #Histoire#Union européenne
Les présidents de la république #Histoire#Politique
Histoire de France #Histoire
SELECT
PPR_TITRE, PPR_ABSTRACT,
SUBSTRING
(
PPR_ABSTRACT, N.NUM +
1
, LEN(
PPR_ABSTRACT)
-
N.NUM +
1
)
AS
DECOUPE
FROM
(
SELECT
PPR_TITRE, '#'
+
PPR_ABSTRACT AS
PPR_ABSTRACT
FROM
T_PAPIER_PPR)
T
CROSS
JOIN
T_NUM N
WHERE
SUBSTRING
(
PPR_ABSTRACT, N.NUM, 1
)
=
'#'
;
PPR_TITRE PPR_ABSTRACT DECOUPE
------------------------------------ ------------------------------- -----------------------------
Les derniers roi de France #Histoire#Politique Histoire#Politique
Les derniers roi de France #Histoire#Politique Politique
De Gaulle, un héro de l'histoire #Histoire#Politique#Guerre Histoire#Politique#Guerre
De Gaulle, un héro de l'histoire #Histoire#Politique#Guerre Politique#Guerre
De Gaulle, un héro de l'histoire #Histoire#Politique#Guerre Guerre
Les deux guerres mondiales #Histoire#Guerre Histoire#Guerre
Les deux guerres mondiales #Histoire#Guerre Guerre
Les années 50 en Europe #Histoire#Union européenne Histoire#Union européenne
Les années 50 en Europe #Histoire#Union européenne Union européenne
Les présidents de la république #Histoire#Politique Histoire#Politique
Les présidents de la république #Histoire#Politique Politique
Histoire de France #Histoire Histoire
SELECT
PPR_TITRE, PPR_ABSTRACT,
CASE
WHEN
CHARINDEX(
'#'
, DECOUPE)
>
0
THEN
SUBSTRING
(
DECOUPE, 1
, CHARINDEX(
'#'
, DECOUPE)
-
1
)
ELSE
DECOUPE
END
AS
MOT_CLEF
FROM
(
SELECT
PPR_TITRE, PPR_ABSTRACT,
SUBSTRING
(
PPR_ABSTRACT, N.NUM +
1
, LEN(
PPR_ABSTRACT)
-
N.NUM +
1
)
AS
DECOUPE
FROM
(
SELECT
PPR_TITRE, '#'
+
PPR_ABSTRACT AS
PPR_ABSTRACT
FROM
T_PAPIER_PPR)
T
CROSS
JOIN
T_NUM N
WHERE
SUBSTRING
(
PPR_ABSTRACT, N.NUM, 1
)
=
'#'
)
TT
PPR_TITRE PPR_ABSTRACT MOT_CLEF
------------------------------------ ------------------------------ --------------------
Les derniers roi de France #Histoire#Politique Histoire
Les derniers roi de France #Histoire#Politique Politique
De Gaulle, un héro de l'histoire #Histoire#Politique#Guerre Histoire
De Gaulle, un héro de l'histoire #Histoire#Politique#Guerre Politique
De Gaulle, un héro de l'histoire #Histoire#Politique#Guerre Guerre
Les deux guerres mondiales #Histoire#Guerre Histoire
Les deux guerres mondiales #Histoire#Guerre Guerre
Les années 50 en Europe #Histoire#Union européenne Histoire
Les années 50 en Europe #Histoire#Union européenne Union européenne
Les présidents de la république #Histoire#Politique Histoire
Les présidents de la république #Histoire#Politique Politique
Histoire de France #Histoire Histoire
SELECT
MOT_CLEF, COUNT
(*)
FROM
(
SELECT
PPR_TITRE, PPR_ABSTRACT,
CASE
WHEN
CHARINDEX(
'#'
, DECOUPE)
>
0
THEN
SUBSTRING
(
DECOUPE, 1
, CHARINDEX(
'#'
, DECOUPE)
-
1
)
ELSE
DECOUPE
END
AS
MOT_CLEF
FROM
(
SELECT
PPR_TITRE, PPR_ABSTRACT,
SUBSTRING
(
PPR_ABSTRACT, N.NUM +
1
, LEN(
PPR_ABSTRACT)
-
N.NUM +
1
)
AS
DECOUPE
FROM
(
SELECT
PPR_TITRE, '#'
+
PPR_ABSTRACT AS
PPR_ABSTRACT
FROM
T_PAPIER_PPR)
T
CROSS
JOIN
T_NUM N
WHERE
SUBSTRING
(
PPR_ABSTRACT, N.NUM, 1
)
=
'#'
)
TT )
TTT
GROUP
BY
MOT_CLEF
MOT_CLEF NOMBRE
-------------------------- -----------
Guerre 2
Histoire 6
Politique 3
Union européenne 1