Partager via


Utilisation du type de données hierarchyid

Cette rubrique comprend des informations sur les activités courantes de gestion et d'interrogation d'une arborescence de données hiérarchique.

Dans cette rubrique

Gestion d'une arborescence à l'aide de hierarchyid

Application d'une arborescence

Exemple utilisant CLR

Déplacement de sous-arborescences

Gestion d'une arborescence à l'aide de hierarchyid

Bien qu'une colonne hierarchyid ne représente pas nécessairement une arborescence, une application peut facilement faire en sorte que ce soit le cas.

  • Lorsque vous générez de nouvelles valeurs, effectuez l'une des opérations suivantes :

    • Effectuez le suivi du dernier numéro enfant dans la ligne parent.

    • Calculez le dernier enfant. Cette opération nécessite un index à largeur prioritaire pour être efficace.

  • Appliquez l'unicité en créant un index unique sur la colonne, éventuellement en tant que partie d'une clé de clustering. Pour vous assurer que les valeurs uniques sont insérées, effectuez l'une des opérations suivantes :

    • détermination et insertion de l'unicité de chaque nouveau nœud enfant dans une transaction sérialisable ;

    • détection des échecs de violation de clés uniques et nouvelle tentative.

Exemple utilisant la détection d'erreurs

Dans l'exemple suivant, le code exemple calcule la nouvelle valeur enfant EmployeeId, puis détecte toute violation de clé et retourne au marqueur INS_EMP pour recalculer la valeur EmployeeId de la nouvelle ligne :

USE AdventureWorks ;
GO

CREATE TABLE Org_T1
   (
    EmployeeId hierarchyid PRIMARY KEY,
    OrgLevel AS EmployeeId.GetLevel(),
    EmployeeName nvarchar(50) 
   ) ;
GO

CREATE INDEX Org_BreadthFirst ON Org_T1(OrgLevel, EmployeeId)
GO

CREATE PROCEDURE AddEmp(@mgrid hierarchyid, @EmpName nvarchar(50) ) 
AS
BEGIN
    DECLARE @last_child hierarchyid
INS_EMP: 
    SELECT @last_child = MAX(EmployeeId) FROM Org_T1 
    WHERE EmployeeId.GetAncestor(1) = @mgrid
INSERT Org_T1 (EmployeeId, EmployeeName)
SELECT @mgrid.GetDescendant(@last_child, NULL), @EmpName 
-- On error, return to INS_EMP to recompute @last_child
IF @@error <> 0 GOTO INS_EMP 
END ;
GO

Exemple utilisant une transaction sérialisable

L'index Org_BreadthFirst assure que la détermination de @last_child est une recherche de plage. En plus des autres cas d'erreur qu'une application peut être amenée à vérifier, une violation de clé en double après l'insertion indique une tentative d'ajouter plusieurs employés ayant le même ID. Par conséquent, @last_child doit être recalculé. Le code suivant utilise une transaction sérialisable et un index à largeur prioritaire pour calculer la nouvelle valeur de nœud :

CREATE TABLE Org_T2
    (
    EmployeeId hierarchyid PRIMARY KEY,
    LastChild hierarchyid, 
    EmployeeName nvarchar(50) 
    ) ;
GO

CREATE PROCEDURE AddEmp(@mgrid hierarchyid, @EmpName nvarchar(50)) 
AS
BEGIN
DECLARE @last_child hierarchyid
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
BEGIN TRANSACTION 

UPDATE Org_T2 
SET @last_child = LastChild = EmployeeId.GetDescendant(LastChild,NULL)
WHERE EmployeeId = @mgrid
INSERT Org_T2 (EmployeeId, EmployeeName) 
    VALUES(@last_child, @EmpName)
COMMIT
END ;

Le code suivant remplit la table avec trois lignes et retourne les résultats :

INSERT Org_T2 (EmployeeId, EmployeeName) 
    VALUES(hierarchyid::GetRoot(), 'David') ;
GO
AddEmp 0x , 'Sariya'
GO
AddEmp 0x58 , 'Mary'
GO
SELECT * FROM Org_T2

Voici l'ensemble des résultats.

EmployeeId LastChild EmployeeName
---------- --------- ------------
0x        0x58       David
0x58      0x5AC0     Sariya
0x5AC0    NULL       Mary

Icône de flèche utilisée avec le lien Retour en hautRetour au début

Application d'une arborescence

Les exemples ci-dessus illustrent comment une application peut garantir la conservation d'une arborescence. Pour appliquer une arborescence via des contraintes, une colonne calculée qui définit le parent de chaque nœud peut être créée avec une contrainte de clé étrangère vers l'ID de clé primaire.

CREATE TABLE Org_T3
(
   EmployeeId hierarchyid PRIMARY KEY,
   ParentId AS EmployeeId.GetAncestor(1) PERSISTED  
      REFERENCES Org_T3(EmployeeId),
   LastChild hierarchyid, 
   EmployeeName nvarchar(50)
)
GO

Cette méthode d'application d'une relation est préférable lorsqu'un code non fiable pour maintenir l'arborescence hiérarchique dispose d'un accès DML direct à la table. Cette méthode est susceptible de réduire les performances car la contrainte doit être vérifiée à chaque opération DML.

Icône de flèche utilisée avec le lien Retour en hautRetour au début

Exemple utilisant CLR

La recherche de l'ancêtre commun le plus bas est une opération courante impliquant deux nœuds dans une hiérarchie. Cela peut être écrit dans Transact-SQL ou CLR, car le type hierarchyid est disponible pour les deux. L'utilisation de CLR est recommandée car les performances seront plus rapides.

Utilisez le code CLR suivant pour rechercher des ancêtres de liste et trouver l'ancêtre commun le plus bas :

using System;
using System.Collections;
using System.Text;
using Microsoft.SqlServer.Server;
using Microsoft.SqlServer.Types;

public partial class HierarchyId_Operations
{
    [SqlFunction(FillRowMethodName = "FillRow_ListAncestors")]
    public static IEnumerable ListAncestors(SqlHierarchyId h)
    {
        while (!h.IsNull)
        {
            yield return (h);
            h = h.GetAncestor(1);
        }
    }
    public static void FillRow_ListAncestors(Object obj, out SqlHierarchyId ancestor)
    {
        ancestor = (SqlHierarchyId)obj;
    }

    public static HierarchyId CommonAncestor(SqlHierarchyId h1, HierarchyId h2)
    {
        while (!h1.IsDescendant(h2))
            h1 = h1.GetAncestor(1);
        
        return h1;
    }
}

Pour utiliser les méthodes ListAncestor et CommonAncestor dans les exemples Transact-SQL suivants, générez la DLL et créez l'assembly HierarchyId_Operations dans SQL Server en exécutant un code semblable à celui-ci :

CREATE ASSEMBLY HierarchyId_Operations 
FROM '<path to DLL>\ListAncestors.dll'
GO

Icône de flèche utilisée avec le lien Retour en hautRetour au début

Répertorier les ancêtres

La création d'une liste d'ancêtres d'un nœud est une opération courante, par exemple pour afficher la position au sein d'une organisation. Pour ce faire, il est par exemple possible d'utiliser une fonction table à l'aide de la classe HierarchyId_Operations définie ci-dessus :

Utilisation de Transact-SQL :

CREATE FUNCTION ListAncestors (@node hierarchyid)
RETURNS TABLE (node hierarchyid)
AS
EXTERNAL NAME HierarchyId_Operations.HierarchyId_Operations.ListAncestors
GO

Exemple d'utilisation :

DECLARE @h hierarchyid
SELECT @h = OrgNode 
FROM HumanResources.EmployeeDemo  
WHERE LoginID = 'adventure-works\janice0' -- /1/1/5/2/

SELECT LoginID, OrgNode.ToString() AS LogicalNode
FROM HumanResources.EmployeeDemo AS ED
JOIN ListAncestors(@h) AS A 
   ON ED.OrgNode = A.Node
GO

Recherche de l'ancêtre commun le plus bas

À l'aide de la classe HierarchyId_Operations définie ci-dessus, créez la fonction Transact-SQL suivante pour rechercher l'ancêtre commun le plus bas impliquant deux nœuds dans une hiérarchie :

CREATE FUNCTION CommonAncestor (@node1 hierarchyid, @node2 hierarchyid)
RETURNS hierarchyid
AS
EXTERNAL NAME HierarchyId_Operations.HierarchyId_Operations.CommonAncestor
GO

Exemple d'utilisation :

DECLARE @h1 hierarchyid, @h2 hierarchyid

SELECT @h1 = OrgNode 
FROM  HumanResources.EmployeeDemo 
WHERE LoginID = 'adventure-works\jossef0' -- Node is /1/1/3/

SELECT @h2 = OrgNode 
FROM HumanResources.EmployeeDemo  
WHERE LoginID = 'adventure-works\janice0' -- Node is /1/1/5/2/

SELECT OrgNode.ToString() AS LogicalNode, LoginID 
FROM HumanResources.EmployeeDemo  
WHERE OrgNode = dbo.CommonAncestor(@h1, @h2) ;

Le nœud résultant est /1/1/

Icône de flèche utilisée avec le lien Retour en hautRetour au début

Déplacement de sous-arborescences

Une autre opération courante concerne le déplacement de sous-arborescences. La procédure suivante prend la sous-arborescence de @oldMgr pour en faire une sous-arborescence de @newMgr (en y incluant @oldMgr).

CREATE PROCEDURE MoveOrg(@oldMgr nvarchar(256), @newMgr nvarchar(256) )
AS
BEGIN
DECLARE @nold hierarchyid, @nnew hierarchyid
SELECT @nold = OrgNode FROM HumanResources.EmployeeDemo WHERE LoginID = @oldMgr ;

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
BEGIN TRANSACTION
SELECT @nnew = OrgNode FROM HumanResources.EmployeeDemo WHERE LoginID = @newMgr ;

SELECT @nnew = @nnew.GetDescendant(max(OrgNode), NULL) 
FROM HumanResources.EmployeeDemo WHERE OrgNode.GetAncestor(1)=@nnew ;

UPDATE HumanResources.EmployeeDemo  
SET OrgNode = OrgNode.GetReparentedValue(@nold, @nnew)
WHERE @nold.IsDescendant(OrgNode) = 1 ;

COMMIT TRANSACTION
END ;
GO