Uwaga
Dostęp do tej strony wymaga autoryzacji. Może spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Dotyczy:SQL Server
Azure SQL Database
Azure SQL Managed Instance
SQL Database w Microsoft Fabric
Wbudowany typ danych hierarchyid ułatwia przechowywanie danych hierarchicznych i wykonywanie zapytań o nie. hierarchyid jest zoptymalizowany pod kątem reprezentowania drzew, które są najczęstszym typem danych hierarchicznych.
Dane hierarchiczne są definiowane jako zestaw elementów danych, które są ze sobą powiązane przez relacje hierarchiczne. Relacje hierarchiczne istnieją, gdy jeden element danych jest elementem nadrzędnym innego elementu. Przykłady danych hierarchicznych, które są często przechowywane w bazach danych, obejmują następujące elementy:
- Struktura organizacyjna
- System plików
- Zestaw zadań w projekcie
- Taksonomia terminów językowych
- Wykres łączy między stronami sieci Web
Użyj hierarchyid jako typu danych, aby utworzyć tabele ze strukturą hierarchiczną lub opisać hierarchiczną strukturę danych przechowywanych w innej lokalizacji. Użyj funkcji hierarchyid w Transact-SQL, aby wykonywać zapytania dotyczące danych hierarchicznych i zarządzać nimi.
Kluczowe właściwości
Wartość hierarchyid typ danych reprezentuje pozycję w hierarchii drzewa. Wartości hierarchyid mają następujące właściwości:
Bardzo kompaktowy
Średnia liczba bitów wymaganych do reprezentowania węzła w drzewie z n węzłów zależy od średniego fanoutu (średniej liczby elementów podrzędnych węzła). W przypadku małych fanoutów (0–7) rozmiar wynosi około $6log{A}{n}$ bitów, gdzie A jest średnim fanoutem. Węzeł w hierarchii organizacyjnej liczącej 100 000 osób, z średnią rozpiętością sześciu poziomów, zajmuje około 38 bitów. Jest to zaokrąglane do 40 bitów lub 5 bajtów dla magazynu.
Porównanie jest w pierwszej kolejności
Biorąc pod uwagę dwie wartości hierarchyid
a
ib
,a < b
oznacza, żea
jest przedb
w trasie przeszukiwania drzewa w głąb. Indeksy w hierarchii typy danych są w pierwszej kolejności, a węzły blisko siebie w głębi pierwszego przechodzenia są przechowywane w pobliżu siebie. Na przykład dzieci rekordu są przechowywane obok tego rekordu.Obsługa dowolnych wstawiania i usuwania
Za pomocą metody GetDescendant (aparatu bazy danych) zawsze można wygenerować element równorzędny po prawej stronie dowolnego węzła, z lewej strony dowolnego węzła lub między dwoma elementami równorzędnymi. Właściwość porównania jest zachowywana, gdy dowolna liczba węzłów zostanie wstawiona lub usunięta z hierarchii. Większość wstawiania i usuwania zachowuje właściwość kompaktowania. Jednak wstawienia między dwoma węzłami generują wartości hierarchyid z nieco mniej zwartą reprezentacją.
Ograniczenia
Typ danych hierarchyid ma następujące ograniczenia:
Kolumna typu hierarchyid nie reprezentuje automatycznie drzewa. Do aplikacji należy generowanie i przypisywanie wartości hierarchyid w taki sposób, aby żądana relacja między wierszami została odzwierciedlona w wartościach. Niektóre aplikacje mogą mieć kolumnę typu hierarchyid, która wskazuje lokalizację w hierarchii zdefiniowanej w innej tabeli.
Do aplikacji należy zarządzanie współbieżnością podczas generowania i przypisywania wartości hierarchyid . Nie ma gwarancji, że wartości hierarchyid w kolumnie są unikatowe, chyba że aplikacja używa unikatowego ograniczenia klucza lub wymusza unikatowość za pomocą własnej logiki.
Relacje hierarchiczne, które są reprezentowane przez wartości hierarchyid, nie są egzekwowane jak relacje klucza obcego. Istnieje możliwość, a czasami jest to odpowiednie, aby mieć relację hierarchiczną, gdzie
A
ma podrzędny elementB
, a następnieA
jest usunięty, pozostawiającB
z relacją do nieistniejącego rekordu. Jeśli takie zachowanie jest niedopuszczalne, aplikacja musi wykonywać zapytania dotyczące potomków przed usunięciem rodziców.
Kiedy należy używać alternatyw dla hierarchyid
Dwie alternatywy dla hierarchiiid reprezentujące dane hierarchiczne to:
- Rodzic/dziecko
- XML
hierarchyid jest ogólnie lepszy od tych alternatyw. Jednak istnieją konkretne sytuacje, szczegółowo opisane w tym artykule, gdzie alternatywy są prawdopodobnie lepsze.
Rodzic/dziecko
W przypadku korzystania z podejścia nadrzędnego/podrzędnego każdy wiersz zawiera odwołanie do elementu nadrzędnego. W poniższej tabeli zdefiniowano typową tabelę, która zawiera wiersze nadrzędne i podrzędne w relacji nadrzędnej/podrzędnej:
USE AdventureWorks2022;
GO
CREATE TABLE ParentChildOrg (
BusinessEntityID INT PRIMARY KEY,
ManagerId INT FOREIGN KEY REFERENCES ParentChildOrg(BusinessEntityID),
EmployeeName NVARCHAR(50)
);
GO
Porównanie relacji nadrzędny/podrzędny i hierarchyid dla typowych operacji:
- Zapytania poddrzewa są znacznie szybsze dzięki hierarchyidowi.
- Zapytania dotyczące bezpośrednich potomków są nieco wolniejsze z hierarchyid.
- Przenoszenie węzłów nie-liściastych jest wolniejsze z hierarchyid.
- Wstawianie węzłów innych niż liście oraz wstawianie lub przenoszenie węzłów liścia mają taką samą złożoność jak hierarchyid.
Relacja rodzic/dziecko może być lepsza, gdy istnieją następujące warunki:
Rozmiar klucza jest krytyczny. W przypadku tej samej liczby węzłów wartość hierarchyid jest równa lub większa niż wartość rodziny całkowitej (smallint, int, bigint). Jest to tylko powód używania struktury nadrzędnej/podrzędnej w rzadkich przypadkach, ponieważ hierarchyid ma znacznie lepszą lokalność operacji wejścia/wyjścia oraz złożoność procesora niż typowe wyrażenia tabel, które trzeba stosować przy strukturze nadrzędnej/podrzędnej.
Zapytania rzadko wykonują zapytania między poziomami hierarchii. Innymi słowy zapytania zwykle dotyczą tylko jednego punktu w hierarchii. W takich przypadkach kolokacja nie jest ważna. Na przykład relacja nadrzędny/podrzędny jest bardziej efektywna, gdy tabela organizacji jest używana tylko do przetwarzania listy płac dla poszczególnych pracowników.
Poddrzewa nienastawne są często przenoszone, a wydajność jest bardzo ważna. W reprezentacji nadrzędnej/podrzędnej zmiana lokalizacji wiersza w hierarchii ma wpływ na pojedynczy wiersz. Zmiana lokalizacji wiersza w użyciu hierarchyid ma wpływ na n wierszy, gdzie n jest liczbą węzłów w poddrzewie przenoszonym.
Jeśli poddrzewa wewnętrzne są często przenoszone i jeśli wydajność ma znaczenie, ale duża część z nich następuje na dobrze zdefiniowanym poziomie hierarchii, można rozważyć podział wyższych i niższych poziomów na dwie hierarchie. To sprawia, że wszystkie ruchy przenoszą się na poziomy liści wewnątrz wyższej hierarchii. Rozważmy na przykład hierarchię witryn sieci Web hostowanych przez usługę. Witryny zawierają wiele stron rozmieszczonych w sposób hierarchiczny. Hostowane witryny mogą być przenoszone do innych lokalizacji w hierarchii lokacji, ale strony podrzędne są rzadko zmieniane. Może to być reprezentowane za pośrednictwem:
CREATE TABLE HostedSites ( SiteId HIERARCHYID, PageId HIERARCHYID ); GO
XML
Dokument XML jest drzewem, dlatego pojedyncze wystąpienie typu danych XML może reprezentować pełną hierarchię. W programie SQL Server po utworzeniu indeksu XML wartości hierarchyid są używane wewnętrznie do reprezentowania pozycji w hierarchii.
Użycie typu danych XML może być lepsze, jeśli spełnione są wszystkie następujące warunki:
- Pełna hierarchia jest zawsze przechowywana i pobierana.
- Dane są używane w formacie XML przez aplikację.
- Wyszukiwanie predykatów jest bardzo ograniczone i nie jest krytyczne dla wydajności.
Jeśli na przykład aplikacja śledzi wiele organizacji, zawsze przechowuje i pobiera pełną hierarchię organizacyjną i nie wykonuje zapytań w jednej organizacji, tabela następującego formularza może mieć sens:
CREATE TABLE XMLOrg (
Orgid INT,
Orgdata XML
);
GO
Strategie indeksowania dla danych hierarchicznych
Istnieją dwie strategie indeksowania danych hierarchicznych:
Głębokość pierwsza
Indeks szczegółowy przechowuje wiersze w poddrzewie w pobliżu siebie. Na przykład wszyscy pracownicy, którzy raportują bezpośrednio do menedżera, są przechowywani w pobliżu rekordu ich menedżera.
W szczegółowym indeksie wszystkie węzły w poddrzewie węzła są kolokowane. W związku z tym indeksy szczegółowe są wydajne w przypadku odpowiadania na zapytania dotyczące poddrzew, takich jak: "Znajdź wszystkie pliki w tym folderze i jego podfolderach"
Przeszukiwanie wszerz
Indeks przeganiający w poziomie przechowuje wiersze z każdego poziomu hierarchii razem. Na przykład rekordy pracowników, którzy bezpośrednio raportują do tego samego menedżera, są przechowywane w pobliżu siebie.
W indeksie po raz pierwszy wszystkie bezpośrednie elementy podrzędne węzła są kolokowane. Ze względu na to indeksy przeszukiwania wszerz są wydajne do odpowiadania na zapytania dotyczące bezpośrednich dzieci, takich jak: "Znajdź wszystkich pracowników podlegających bezpośredniemu menedżerowi"
Decyzja o wyborze podejścia przeszukiwania wszerz, w głąb lub obu oraz którym podejściu przydzielić klucz klastrowania (jeśli w ogóle) zależy od względnego znaczenia powyższych typów zapytań oraz względnego znaczenia SELECT
w porównaniu do operacji DML. Aby zapoznać się ze szczegółowym przykładem strategii indeksowania, zobacz Samouczek: używanie typu danych hierarchyid.
Tworzenie indeksów
Metodę GetLevel() można użyć do utworzenia zakresu pierwszej kolejności. W poniższym przykładzie tworzone są zarówno indeksy przeszukiwania wszerz, jak i przeszukiwania w głąb.
USE AdventureWorks2022;
GO
CREATE TABLE Organization (
BusinessEntityID HIERARCHYID,
OrgLevel AS BusinessEntityID.GetLevel(),
EmployeeName NVARCHAR(50) NOT NULL
);
GO
CREATE CLUSTERED INDEX Org_Breadth_First
ON Organization (OrgLevel, BusinessEntityID);
GO
CREATE UNIQUE INDEX Org_Depth_First
ON Organization (BusinessEntityID);
GO
Przykłady
Przykłady kodu w tym artykule korzystają z przykładowej bazy danych AdventureWorks2022
lub AdventureWorksDW2022
, którą można pobrać ze strony głównej Przykładów programu Microsoft SQL Server i projektów społeczności.
Przykład podstawowy
Poniższy przykład jest celowo uproszczony, aby ułatwić rozpoczęcie pracy. Najpierw utwórz tabelę do przechowywania niektórych danych geograficznych.
CREATE TABLE BasicDemo (
[Level] HIERARCHYID NOT NULL,
Location NVARCHAR(30) NOT NULL,
LocationType NVARCHAR(9) NULL
);
Teraz wstaw dane dla niektórych kontynentów, krajów/regionów, stanów i miast.
INSERT BasicDemo
VALUES ('/1/', 'Europe', 'Continent'),
('/2/', 'South America', 'Continent'),
('/1/1/', 'France', 'Country'),
('/1/1/1/', 'Paris', 'City'),
('/1/2/1/', 'Madrid', 'City'),
('/1/2/', 'Spain', 'Country'),
('/3/', 'Antarctica', 'Continent'),
('/2/1/', 'Brazil', 'Country'),
('/2/1/1/', 'Brasilia', 'City'),
('/2/1/2/', 'Bahia', 'State'),
('/2/1/2/1/', 'Salvador', 'City'),
('/3/1/', 'McMurdo Station', 'City');
Wybierz dane, dodając kolumnę, która konwertuje dane na wartość tekstową, którą można łatwo zrozumieć. To zapytanie porządkuje również wynik według typu danych hierarchyid .
SELECT CAST([Level] AS NVARCHAR(100)) AS [Converted Level],
*
FROM BasicDemo
ORDER BY [Level];
Oto zestaw wyników.
Converted Level Level Location LocationType
--------------- -------- --------------- ---------------
/1/ 0x58 Europe Continent
/1/1/ 0x5AC0 France Country
/1/1/1/ 0x5AD6 Paris City
/1/2/ 0x5B40 Spain Country
/1/2/1/ 0x5B56 Madrid City
/2/ 0x68 South America Continent
/2/1/ 0x6AC0 Brazil Country
/2/1/1/ 0x6AD6 Brasilia City
/2/1/2/ 0x6ADA Bahia State
/2/1/2/1/ 0x6ADAB0 Salvador City
/3/ 0x78 Antarctica Continent
/3/1/ 0x7AC0 McMurdo Station City
Hierarchia ma prawidłową strukturę, mimo że nie jest spójna wewnętrznie. Bahia jest jedynym stanem. Pojawia się w hierarchii jako element równorzędny miasta Brasilia. Podobnie stacja McMurdo nie ma kraju/regionu nadrzędnego. Użytkownicy muszą zdecydować, czy ten typ hierarchii jest odpowiedni do ich użycia.
Dodaj kolejny wiersz i wybierz wyniki.
INSERT BasicDemo
VALUES ('/1/3/1/', 'Kyoto', 'City'),
('/1/3/1/', 'London', 'City');
SELECT CAST([Level] AS NVARCHAR(100)) AS [Converted Level],
*
FROM BasicDemo
ORDER BY [Level];
Pokazuje to więcej możliwych problemów. Kioto można wstawić jako poziom /1/3/1/
, mimo że nie ma poziomu /1/3/
nadrzędnego . Zarówno Londyn, jak i Kioto mają tę samą wartość dla hierarchyid. Ponownie użytkownicy muszą zdecydować, czy tego typu hierarchia jest odpowiednia do ich użycia, oraz zablokować wartości, które są niewłaściwe dla ich zastosowania.
Ponadto ta tabela nie korzystała z górnej części hierarchii '/'
. Pominięto go, ponieważ nie ma jednego wspólnego przodka kontynentów. Możesz dodać jedną, dodając całą planetę.
INSERT BasicDemo
VALUES ('/', 'Earth', 'Planet');
Powiązane zadania
Migrowanie z modelu rodzic/dziecko do hierarchyid
Większość drzew jest reprezentowana przy użyciu struktury nadrzędnej/podrzędnej. Najprostszym sposobem migracji ze struktury nadrzędny/podrzędny do tabeli przy użyciu hierarchyid jest skorzystanie z kolumny tymczasowej lub tabeli tymczasowej do śledzenia liczby węzłów na każdym poziomie hierarchii. Aby zapoznać się z przykładem migracji tabeli nadrzędnej/podrzędnej, zobacz część 1 Samouczka: używanie hierarchyid typu danych.
Zarządzanie drzewem przy użyciu hierarchyid
Chociaż kolumna hierarchyid nie musi reprezentować drzewa, aplikacja może łatwo upewnić się, że to zrobi.
Podczas generowania nowych wartości wykonaj jedną z następujących czynności:
- Śledź ostatni numer dziecka w wierszu rodzica.
- Oblicz ostatnie dziecko. Skuteczne wykonanie tej czynności wymaga indeksu przeszukiwania wszerz.
Wymuś unikatowość, tworząc unikatowy indeks w kolumnie, być może jako część klucza klastrowania. Aby upewnić się, że wstawiane są unikatowe wartości, wykonaj jedną z następujących czynności:
- Wykryj błędy naruszenia unikalnego klucza i ponów próbę.
- Określ unikatowość każdego nowego węzła podrzędnego i wstaw go w ramach transakcji możliwej do serializacji.
Przykład użycia wykrywania błędów
W poniższym przykładzie kod testowy oblicza nową wartość elementu podrzędnego EmployeeId
, a następnie wykrywa naruszenie klucza i powraca do znacznika INS_EMP
, aby ponownie obliczyć wartość EmployeeId
dla nowego wiersza.
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 INTO 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
Przykład użycia transakcji możliwej do serializacji
Indeks Org_BreadthFirst
zapewnia, że definicja @last_child
korzysta z wyszukiwania w zakresie. Oprócz innych przypadków błędów aplikacja może chcieć sprawdzić, zduplikowane naruszenie klucza po wstawieniu wskazuje próbę dodania wielu pracowników o tym samym identyfikatorze i dlatego @last_child
należy ponownie skompilować. Poniższy kod oblicza nową wartość węzła w ramach transakcji możliwej do serializacji:
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;
SELECT @last_child = EmployeeId.GetDescendant(LastChild, NULL)
FROM Org_T2
WHERE EmployeeId = @mgrid;
UPDATE Org_T2
SET LastChild = @last_child
WHERE EmployeeId = @mgrid;
INSERT Org_T2 (EmployeeId, EmployeeName)
VALUES (@last_child, @EmpName);
COMMIT;
END;
Poniższy kod wypełnia tabelę trzema wierszami i zwraca wyniki:
INSERT Org_T2 (EmployeeId, EmployeeName)
VALUES (HIERARCHYID::GetRoot(), 'David');
GO
EXECUTE AddEmp 0x, 'Sariya';
GO
EXECUTE AddEmp 0x58, 'Mary';
GO
SELECT * FROM Org_T2
Oto zestaw wyników.
EmployeeId LastChild EmployeeName
---------- --------- ------------
0x 0x58 David
0x58 0x5AC0 Sariya
0x5AC0 NULL Mary
Wymuszanie drzewa
W poprzednich przykładach pokazano, jak aplikacja może zapewnić utrzymanie drzewa. Aby wymusić strukturę drzewa przy użyciu ograniczeń, można stworzyć obliczoną kolumnę, która definiuje element nadrzędny każdego węzła, z ograniczeniem klucza obcego odnoszącym się z powrotem do identyfikatora klucza podstawowego.
CREATE TABLE Org_T3 (
EmployeeId HIERARCHYID PRIMARY KEY,
ParentId AS EmployeeId.GetAncestor(1) PERSISTED FOREIGN KEY REFERENCES Org_T3(EmployeeId),
LastChild HIERARCHYID,
EmployeeName NVARCHAR(50)
);
GO
Ta metoda wymuszania relacji jest preferowana, gdy kod, któremu nie można zaufać w kwestii zarządzania drzewem hierarchicznym, ma bezpośredni dostęp DML do tabeli. Jednak ta metoda może zmniejszyć wydajność, ponieważ ograniczenie musi być sprawdzane dla każdej operacji DML.
Znajdowanie przodków przy użyciu środowiska CLR
Typową operacją obejmującą dwa węzły w hierarchii jest znalezienie najniższego wspólnego przodka. To zadanie można napisać w Transact-SQL lub CLR, ponieważ typ hierarchyid jest dostępny w obu tych przypadkach. Zaleca się użycie środowiska CLR, ponieważ wydajność jest szybsza.
Użyj następującego kodu CLR, aby wyświetlić listę przodków i znaleźć najniższego wspólnego przodka.
using System;
using System.Collections;
using System.Text;
using Microsoft.SqlServer.Server; // SqlFunction Attribute
using Microsoft.SqlServer.Types; // SqlHierarchyId
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.IsDescendantOf(h2))
{
h1 = h1.GetAncestor(1);
}
return h1;
}
}
Aby użyć metody ListAncestor
i CommonAncestor
w następujących przykładach Transact-SQL, skompiluj bibliotekę DLL i utwórz zestaw HierarchyId_Operations
na serwerze SQL przez wykonanie następującego przykładu kodu.
CREATE ASSEMBLY HierarchyId_Operations
FROM '<path to DLL>\ListAncestors.dll';
GO
Lista przodków
Tworzenie listy elementów podrzędnych węzła jest wspólną operacją, na przykład w celu pokazania pozycji w organizacji. Jednym ze sposobów wykonania tej czynności jest użycie funkcji table-valued przy użyciu wcześniej zdefiniowanej klasy HierarchyId_Operations
.
Korzystanie z języka Transact-SQL:
CREATE FUNCTION ListAncestors (@node HIERARCHYID)
RETURNS TABLE (node HIERARCHYID)
AS
EXTERNAL NAME HierarchyId_Operations.HierarchyId_Operations.ListAncestors;
GO
Przykład użycia:
DECLARE @h AS 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
INNER JOIN ListAncestors(@h) AS A
ON ED.OrgNode = A.Node
GO
Znajdowanie najniższego wspólnego przodka
Korzystając z klasy zdefiniowanej HierarchyId_Operations
wcześniej, utwórz następującą funkcję Transact-SQL, aby znaleźć najniższy wspólny element nadrzędny obejmujący dwa węzły w hierarchii:
CREATE FUNCTION CommonAncestor (
@node1 HIERARCHYID,
@node2 HIERARCHYID
)
RETURNS HIERARCHYID
AS
EXTERNAL NAME HierarchyId_Operations.HierarchyId_Operations.CommonAncestor;
GO
Przykład użycia:
DECLARE @h1 AS HIERARCHYID, @h2 AS 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);
Wynikowy węzeł to /1/1/
Przenoszenie poddrzew
Inną typową operacją jest przenoszenie poddrzew. Poniższa procedura przekształca poddrzewo @oldMgr
(włącznie z @oldMgr
) w poddrzewo @newMgr
.
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 OrgNode.IsDescendantOf(@nold) = 1;
COMMIT TRANSACTION;
END;
GO