Les enregistrements des tables nécessitent parfois d'être retournées de manières hierarchiques :
# | Nom | Titre | Niveau |
---|---|---|---|
1 | Pierre Point | PDG | 1 |
2 | Klein Moine | Directeur Marketing | 2 |
3 | Roger Andre | Assistant Marketing | 3 |
4 | Mark Vil | Assistant Marketing | 3 |
5 | Marie Clon | Directrice RH | 2 |
6 | Elise Tant | Assistante RH | 3 |
TSQL permet toute sorte de manipulations hierarchiques.
Pour cela, 2 moyens s'offrent à nous :
Le type de données HierarchiID est intégralement conçu pour
manipuler les hyerachies de données au sein des tables.
Il stocke l'information de manière similaire à varbinary,
tout deux stoquent en binaire les données.
Voici comment l'utiliser.
1. On commence par créer notre table :
CREATE TABLE employe
(
emp_id int PRIMARY KEY NOT NULL,
emp_hy hierarchyid NOT NULL,
emp_nom varchar(40) NOT NULL,
emp_pre varchar(40) NOT NULL,
emp_titre varchar(40) NOT NULL
);
2. On y insère les données suivantes :
INSERT INTO employe VALUES (1,
'/1/', 'Point', 'Pierre', 'PDG')
INSERT INTO employe VALUES (8,
'/1/1/', 'Moine', 'Klein', 'Directeur Marketing')
INSERT INTO employe VALUES (2,
'/1/1/1/', 'Andre', 'Roger', 'Assistant Marketing')
INSERT INTO employe VALUES (3,
'/1/1/2/', 'Vil', 'Mark', 'Assistant Marketing')
INSERT INTO employe VALUES (7,
'/1/2/', 'Clon', 'Marie', 'Directrice RH')
INSERT INTO employe VALUES (5,
'/1/2/1/', 'Tant', 'Elise', 'Assistante RH')
Ici, on insère directement sous forme d'une chaine de caractères l'emplacement
hyerachique. Une conversion implicite est faite automatiquement.
3. Finalement, on récupère les donneés :
SELECT CAST(emp_hy AS nvarchar(100)) AS Level, emp_id,
emp_nom, emp_pre, emp_titre
FROM employe ORDER BY emp_hy;
Là, on convertit vers un type texte afin qu'il soit lisible, autrement des données hexadécimales
s'afficheront.
La méthode .ToString() qui sera expliquée plus bas permet d'arriver au même résultat.
Résultat de la requête :
# | Level | emp_id | emp_nom | emp_pre | emp_titre |
---|---|---|---|---|---|
1 | /1/ | 1 | Point | Pierre | PDG |
2 | /1/1/ | 8 | Moine | Klein | Directeur Marketing |
3 | /1/1/1/ | 2 | Andre | Roger | Assistant Marketing |
4 | /1/1/2/ | 3 | Vil | Mark | Assistant Marketing |
5 | /1/2/ | 7 | Clon | Marie | Directrice RH |
6 | /1/2/1/ | 5 | Tant | Elise | Assistante RH |
Quelques méthodes parmis les plus pratiques :
SELECT emp_hy.ToString(), emp_pre WHERE
emp_nom = 'Point'
SELECT emp_hy.ToString(), emp_pre WHERE
emp_hy = HierarchiID::GetRoot()
Résultat req. : /1/, Pierre
SELECT emp_hy.GetLevel() AS
Level, emp_id, emp_nom, emp_pre, emp_titre
FROM employe ORDER BY
emp_hy
Résultat :
# | Level | emp_id | emp_nom | emp_pre | emp_titre |
---|---|---|---|---|---|
1 | 1 | 1 | Point | Pierre | PDG |
2 | 2 | 8 | Moine | Klein | Directeur Marketing |
3 | 3 | 2 | Andre | Roger | Assistant Marketing |
4 | 3 | 3 | Vil | Mark | Assistant Marketing |
5 | 2 | 7 | Clon | Marie | Directrice RH |
6 | 3 | 5 | Tant | Elise | Assistante RH |
Liste complète des méthodes :
https://docs.microsoft.com/fr-fr/sql/t-sql/data-types/hierarchyid-data-type-method-reference?view=sql-server-ver15
Soit, les 3 tables suivantes :
vln_id | vln_nom | vln_pre |
---|---|---|
1 | Markus | Pan |
2 | Helene | Vram |
3 | Adam | Pion |
vio_id | vln_id |
---|---|
1 | 2 |
3 | 1 |
6 | 3 |
vio_id | vio_hy | vio_nom | vio_niveau |
---|---|---|---|
1 | /3/1/ | Hedersine HEV1 | Debutant |
2 | /2/1/ | Goetz Menuett-Heritage 98 | Avance |
3 | /1/1/ | Bavarian 1720 Stradivarius | Professionnel |
4 | /1/2/ | Wessex XV Series | Professionnel |
5 | /2/2/ | Hedersine Whitmarsh | Avance |
6 | /3/2/ | Stentor student | Debutant |
Requête à executer :
SELECT vio.vio_hy.ToString() AS
Level, vio.vio_nom, vio.vio_niveau, vln.vln_nom, vln.vln_pre
FROM violon AS vio JOIN
jouer AS j ON j.vio_id =
vio.vio_id JOIN
violoniste AS vln ON j.vln_id
=
vln.vln_id ORDER BY vio.vio_hy
Résultat :
Level | vio_nom | vio_niveau | vln_nom | vln_pre |
---|---|---|---|---|
/1/1/ | Bavarian 1720 Stradivarius | Professionnel | Markus | Pan |
/3/1/ | Hedersine HEV1 | Debutant | Helene | Vram |
/3/2/ | Stentor student | Debutant | Adam | Pion |
Voici une feuille d'aide que j'ai pu conceptualiser lors de mon stage.
Celle-ci regroupe toutes sortes de manipulations liées aux hyerarchies.
-- Ajout d'une colonne hiérarchique dans une table Alter table USINE ADD USI_H hierarchyid -- Mis à jour d'un enregistement UPDATE dbo.USINE SET USI_H = '/1/' WHERE USI_ID = 38 -- Comparaison SELECT USI_H.ToString() FROM USINE WHERE [USI_H] < '/3/' SELECT USI_H.ToString() FROM USINE WHERE [USI_H] > '/2/1/' /* Récupèrer l'élément parent */ SELECT USI_ID, USI_NOM, USI_H.ToString() from USINE where ( SELECT USI_H.GetAncestor(1) as parent FROM USINE WHERE USI_ID = 68 ) = USI_H /* Récupèrer l'élément + les éléments enfants */ SELECT USI_ID, USI_NOM, USI_H.ToString() from USINE WHERE USI_H.IsDescendantOf( (SELECT USI_H.ToString() from USINE WHERE USI_ID = 40) ) = 1 /* Récupèrer l'élément + tous les éléments parents */ SELECT t1.USI_H.ToString() as Path, t1.USI_ID FROM (SELECT * FROM USINE WHERE USI_ID = 68) t2, USINE t1 WHERE t2.USI_H.IsDescendantOf(t1.USI_H) = 1 ORDER BY Path /* Récupère tous les éléments d'un niveau précis */ SELECT USI_ID, USI_NOM, USI_H.ToString() FROM USINE WHERE USI_H.GetLevel() = 1 --======================================================================================= -- Retourne le future parent niveau 1 de la hiérarchie -- Ex: Si le dernier noeud d'une hierarchie est /107/6/ : retourne /108/ -- /16/ : retourne /17/ -- /10/1/2/: retourne /11/ -- --======================================================================================= SELECT top 1 hierarchyid::GetRoot().GetDescendant(t1.hyer ,null).ToString() as hyer from ( SELECT top 1 t0.hyer.GetAncestor(t0.niv - 1) as hyer from ( SELECT top 1 USI_H.GetLevel() as niv, USI_H as hyer from USINE order by USI_H desc ) t0 ) t1, USINE --======================================================================================= -- Retourne le future frère du dernier enregistrement de la hiérarchie -- Ex: Si le dernier noeud est /10/1/4/ : retourne /10/1/5/ -- /2/2/ : retourne /2/3/ -- /20/ : retourne /21/ -- -- Possibilité de changer le noeud de départ en changant la condition 1=1 --======================================================================================= SELECT top 1 T1.hyer.GetAncestor(1).GetDescendant(T1.hyer ,null).ToString() AS hyer FROM ( SELECT top 1 USI_H as hyer from USINE where 1=1 order by hyer desc ) T1, USINE order by USI_H desc --============================================================================================================ -- Retourne le future dernier fils d'un enregistrement de la hiérarchie -- Ex: Si le noeud noeud est /10/1/ ET que le dernier fils de ce noeud est /10/1/7 : retourne /10/1/8/ -- /2/2/3/ ET que le dernier fils de ce noeud est /2/2/3/4 : retourne /2/2/3/5 -- /20/ ET que le dernier fils de ce noeud est /20/3 : retourne /20/4/ -- -- Possibilité de changer le noeud de départ en changant la condition 1=1 --=========================================================================================================== SELECT top 1 T1.hyer.GetAncestor(1).GetDescendant(T1.hyer ,null).ToString() AS hyer FROM ( SELECT top 1 USI_H as hyer from USINE WHERE USI_H.IsDescendantOf( ( SELECT USI_H from USINE WHERE USI_ID = 35 ) ) = 1 order by hyer desc ) T1, USINE order by USI_H desc
La 2ᵉ méthode consiste à utiliser une colonne identifiant, de type INT
et de faire une boucle afin de remonter à l'enregistrement parent.
Voici un exemple :
WITH descendant AS (
SELECT EMPLOYE.EMP_ID,
EMPLOYE.EMP_MAN_ID,
EMPLOYE.EMP_NOM,
EMPLOYE.EMP_PRE,
EMPLOYE.EMP_TITRE,
LEFT(CONCAT(CONVERT(nvarchar(20), 1), ' - ', EMPLOYE.EMP_TRI),20) AS ordre
FROM EMPLOYE
WHERE EMPLOYE.EMP_MAN_ID IS null
UNION ALL
SELECT EMPLOYE.EMP_ID,
EMPLOYE.EMP_MAN_ID,
EMPLOYE.EMP_NOM,
EMPLOYE.EMP_PRE,
EMPLOYE.EMP_TITRE,
LEFT(CONCAT(descendant.ordre , ' - ', EMPLOYE.EMP_TRI),20) AS ordre
FROM EMPLOYE, descendant
WHERE descendant.EMP_ID = EMPLOYE.EMP_MAN_ID
)
SELECT descendant.EMP_ID,
descendant.EMP_MAN_ID AS ID_PERE,
descendant.EMP_NOM,
descendant.EMP_PRE,
descendant.EMP_TITRE,
descendant.ordre AS NIVEAU
FROM descendant
ORDER BY NIVEAU ASC