Sqlserver
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> Sqlserver

Cách sử dụng SQL Server HierarchyID thông qua các ví dụ đơn giản

Bạn vẫn giữ thiết kế cha / con hay muốn thử một cái gì đó mới, chẳng hạn như SQL Server hierarchyID? Chà, nó thực sự mới vì hierarchyID đã là một phần của SQL Server từ năm 2008. Tất nhiên, bản thân tính mới không phải là một lập luận thuyết phục. Nhưng lưu ý rằng Microsoft đã thêm tính năng này để thể hiện mối quan hệ một-nhiều với nhiều cấp theo cách tốt hơn.

Bạn có thể tự hỏi nó tạo ra sự khác biệt nào và lợi ích nào bạn nhận được khi sử dụng hierarchyID thay vì các mối quan hệ cha / con thông thường. Nếu bạn chưa bao giờ khám phá tùy chọn này, nó có thể gây ngạc nhiên cho bạn.

Sự thật là tôi đã không khám phá tùy chọn này kể từ khi nó được phát hành. Tuy nhiên, cuối cùng khi tôi làm điều đó, tôi thấy đó là một sự đổi mới tuyệt vời. Đó là một mã đẹp hơn, nhưng nó có nhiều thứ hơn trong đó. Trong bài viết này, chúng ta sẽ tìm hiểu về tất cả những cơ hội tuyệt vời đó.

Tuy nhiên, trước khi chúng ta đi sâu vào các đặc thù của việc sử dụng SQL Server hierarchyID, hãy làm rõ ý nghĩa và phạm vi của nó.

SQL Server HierarchyID là gì?

SQL Server hierarchyID là kiểu dữ liệu tích hợp được thiết kế để biểu diễn cây, đây là kiểu dữ liệu phân cấp phổ biến nhất. Mỗi mục trong một cây được gọi là một nút. Ở định dạng bảng, nó là một hàng có một cột kiểu dữ liệu hierarchyID.

Thông thường, chúng tôi chứng minh phân cấp bằng cách sử dụng thiết kế bảng. Một cột ID đại diện cho một nút và một cột khác là viết tắt của cha. Với SQL Server HierarchyID, chúng tôi chỉ cần một cột có kiểu dữ liệu là hierarchyID.

Khi bạn truy vấn một bảng có cột hierarchyID, bạn sẽ thấy các giá trị thập lục phân. Nó là một trong những hình ảnh trực quan của một nút. Một cách khác là một chuỗi:

‘/’ Là viết tắt của nút gốc;

‘/ 1 /’, ‘/ 2 /’, ‘/ 3 /’ hoặc ‘/ n /’ là viết tắt của con cái - trực tiếp con cháu từ 1 đến n;

‘/ 1/1 /’ hoặc ‘/ 1/2 /’ là “con của những đứa trẻ -“ cháu ”. Chuỗi như ‘/ 1/2 /’ có nghĩa là con đầu tiên từ gốc có hai con, lần lượt là hai cháu của gốc.

Dưới đây là một số ví dụ về nó trông như thế nào:

Không giống như các kiểu dữ liệu khác, cột hierarchyID có thể tận dụng các phương thức tích hợp sẵn. Ví dụ:nếu bạn có cột ID cấu trúc phân cấp có tên là RankNode , bạn có thể có cú pháp sau:

RankNode. .

Phương thức SQL Server HierarchyID

Một trong những phương pháp khả dụng là IsDescendantOf . Nó trả về 1 nếu nút hiện tại là con của giá trị ID cấu trúc phân cấp.

Bạn có thể viết mã bằng phương pháp này tương tự như phương pháp bên dưới:

SELECT
 r.RankNode
,r.Rank
FROM dbo.Ranks r
WHERE r.RankNode.IsDescendantOf(0x58) = 1

Các phương pháp khác được sử dụng với hierarchyID như sau:

  • GetRoot - phương thức tĩnh trả về gốc của cây.
  • GetDescendant - trả về một nút con của nút cha.
  • GetAncestor - trả về một ID cấu trúc phân cấp đại diện cho tổ tiên thứ n của một nút nhất định.
  • GetLevel - trả về một số nguyên thể hiện độ sâu của nút.
  • ToString - trả về chuỗi có biểu diễn logic của một nút. Chuỗi ký tự được gọi ngầm khi xảy ra chuyển đổi từ hierarchyID sang kiểu chuỗi.
  • GetReparentedValue - di chuyển một nút từ nút gốc cũ sang nút chính mới.
  • Phân tích cú pháp - hoạt động ngược lại với ToString . Nó chuyển đổi chế độ xem chuỗi của ID cấu trúc phân cấp giá trị thành hệ thập lục phân.

Chiến lược lập chỉ mục HierarchyID SQL Server

Để đảm bảo rằng các truy vấn cho bảng sử dụng hierarchyID chạy nhanh nhất có thể, bạn cần lập chỉ mục cột. Có hai chiến lược lập chỉ mục:

DEPTH-FIRST

Trong chỉ mục đầu tiên theo chiều sâu, các hàng cây con gần nhau hơn. Nó phù hợp với các truy vấn như tìm một phòng ban, các đơn vị con của nó và nhân viên. Một ví dụ khác là một người quản lý và nhân viên của họ được lưu trữ gần nhau hơn.

Trong một bảng, bạn có thể triển khai chỉ mục theo chiều sâu bằng cách tạo chỉ mục được phân cụm cho các nút. Hơn nữa, chúng tôi thực hiện một trong những ví dụ của mình, giống như vậy.

BREADTH-FIRST

Trong chỉ mục theo chiều rộng, các hàng của cùng cấp gần nhau hơn. Nó phù hợp với các truy vấn như tìm tất cả các nhân viên báo cáo trực tiếp của người quản lý. Nếu hầu hết các truy vấn tương tự như vậy, hãy tạo chỉ mục được phân nhóm dựa trên (1) cấp và (2) nút.

Nó phụ thuộc vào yêu cầu của bạn nếu bạn cần chỉ mục đầu tiên theo chiều sâu, đầu tiên theo chiều rộng hoặc cả hai. Bạn cần cân bằng giữa tầm quan trọng của loại truy vấn và các câu lệnh DML mà bạn thực thi trên bảng.

Hạn chế của SQL Server HierarchyID

Rất tiếc, việc sử dụng hierarchyID không thể giải quyết tất cả các vấn đề:

  • Máy chủ SQL không thể đoán con của cha mẹ là gì. Bạn phải xác định cây trong bảng.
  • Nếu bạn không sử dụng một ràng buộc duy nhất, giá trị hierarchyID đã tạo sẽ không phải là duy nhất. Xử lý vấn đề này là trách nhiệm của nhà phát triển.
  • Mối quan hệ của nút cha và nút con không được thực thi giống như mối quan hệ khóa ngoài. Do đó, trước khi xóa một nút, hãy truy vấn bất kỳ phần tử nào hiện có.

Hình dung cấu trúc phân cấp

Trước khi chúng tôi tiếp tục, hãy xem xét một câu hỏi nữa. Nhìn vào tập hợp kết quả với các chuỗi nút, bạn có thấy hệ thống phân cấp khó hình dung cho mắt bạn không?

Đối với tôi, đó là một đồng ý lớn vì tôi không còn trẻ nữa.

Vì lý do này, chúng tôi sẽ sử dụng Power BI và Biểu đồ phân cấp từ Akvelon cùng với các bảng cơ sở dữ liệu của chúng tôi. Chúng sẽ giúp hiển thị thứ bậc trong sơ đồ tổ chức. Tôi hy vọng nó sẽ làm cho công việc dễ dàng hơn.

Bây giờ, hãy bắt tay vào công việc.

Sử dụng SQL Server HierarchyID

Bạn có thể sử dụng HierarchyID với các tình huống kinh doanh sau:

  • Cơ cấu tổ chức
  • Thư mục, thư mục con và tệp
  • Các nhiệm vụ và nhiệm vụ phụ trong một dự án
  • Các trang và trang con của một trang web
  • Dữ liệu địa lý với các quốc gia, khu vực và thành phố

Ngay cả khi tình huống kinh doanh của bạn tương tự như ở trên và bạn hiếm khi truy vấn trên các phần của hệ thống phân cấp, bạn không cần ID phân cấp.

Ví dụ:tổ chức của bạn xử lý bảng lương cho nhân viên. Bạn có cần truy cập vào cây con để xử lý bảng lương của ai đó không? Không có gì. Tuy nhiên, nếu bạn xử lý tiền hoa hồng của những người trong hệ thống tiếp thị đa cấp thì nó có thể khác.

Trong bài đăng này, chúng tôi sử dụng một phần của cơ cấu tổ chức và chuỗi chỉ huy trên một con tàu du lịch. Cơ cấu được điều chỉnh từ sơ đồ tổ chức từ đây. Hãy xem nó trong Hình 4 bên dưới:

Bây giờ bạn có thể hình dung hệ thống phân cấp được đề cập. Chúng tôi sử dụng các bảng dưới đây trong suốt bài đăng này:

  • Tàu - là bảng đại diện cho danh sách các tàu du lịch.
  • Xếp hạng - là bảng cấp bậc thuyền viên. Ở đó, chúng tôi thiết lập cấu trúc phân cấp bằng ID cấu trúc phân cấp.
  • Phi hành đoàn - là danh sách thủy thủ đoàn của mỗi tàu và cấp bậc của họ.

Cấu trúc bảng của từng trường hợp như sau:

CREATE TABLE [dbo].[Vessel](
[VesselId] [int] IDENTITY(1,1) NOT NULL,
[VesselName] [varchar](20) NOT NULL,
 CONSTRAINT [PK_Vessel] PRIMARY KEY CLUSTERED
(
[VesselId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO

CREATE TABLE [dbo].[Ranks](
[RankId] [int] IDENTITY(1,1) NOT NULL,
[Rank] [varchar](50) NOT NULL,
[RankNode] [hierarchyid] NOT NULL,
[RankLevel] [smallint] NOT NULL,
[ParentRankId] [int]   -- this is redundant but we will use this to compare        
                       -- with parent/child
) ON [PRIMARY]
GO

CREATE UNIQUE NONCLUSTERED INDEX [IX_RankId] ON [dbo].[Ranks]
(
[RankId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO

CREATE UNIQUE CLUSTERED INDEX [IX_RankNode] ON [dbo].[Ranks]
(
[RankNode] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO

CREATE TABLE [dbo].[Crew](
[CrewId] [int] IDENTITY(1,1) NOT NULL,
[CrewName] [varchar](50) NOT NULL,
[DateHired] [date] NOT NULL,
[RankId] [int] NOT NULL,
[VesselId] [int] NOT NULL,
 CONSTRAINT [PK_Crew] PRIMARY KEY CLUSTERED
(
[CrewId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO

ALTER TABLE [dbo].[Crew]  WITH CHECK ADD  CONSTRAINT [FK_Crew_Ranks] FOREIGN KEY([RankId])
REFERENCES [dbo].[Ranks] ([RankId])
GO

ALTER TABLE [dbo].[Crew] CHECK CONSTRAINT [FK_Crew_Ranks]
GO

ALTER TABLE [dbo].[Crew]  WITH CHECK ADD  CONSTRAINT [FK_Crew_Vessel] FOREIGN KEY([VesselId])
REFERENCES [dbo].[Vessel] ([VesselId])
GO

ALTER TABLE [dbo].[Crew] CHECK CONSTRAINT [FK_Crew_Vessel]
GO

Chèn dữ liệu bảng bằng SQL Server HierarchyID

Nhiệm vụ đầu tiên khi sử dụng hierarchyID một cách triệt để là thêm các bản ghi vào bảng với hierarchyID cột. Có hai cách để làm điều đó.

Sử dụng chuỗi

Cách nhanh nhất để chèn dữ liệu với hierarchyID là sử dụng chuỗi. Để xem điều này trong thực tế, hãy thêm một số bản ghi vào Xếp hạng bảng.

INSERT INTO dbo.Ranks
([Rank], RankNode, RankLevel)
VALUES
 ('Captain', '/',0)
,('First Officer','/1/',1)
,('Chief Engineer','/2/',1)
,('Hotel Director','/3/',1)
,('Second Officer','/1/1/',2)
,('Second Engineer','/2/1/',2)
,('F&B Manager','/3/1/',2)
,('Chief Housekeeping','/3/2/',2)
,('Chief Purser','/3/3/',2)
,('Casino Manager','/3/4/',2)
,('Cruise Director','/3/5/',2)
,('Third Officer','/1/1/1/',3)
,('Third Engineer','/2/1/1/',3)
,('Asst. F&B Manager','/3/1/1/',3)
,('Asst. Chief Housekeeping','/3/2/1/',3)
,('First Purser','/3/3/1/',3)
,('Asst. Casino Manager','/3/4/1/',3)
,('Music Director','/3/5/1/',3)
,('Asst. Cruise Director','/3/5/2/',3)
,('Youth Staff Director','/3/5/3/',3)

Đoạn mã trên thêm 20 bản ghi vào bảng Xếp hạng.

Như bạn có thể thấy, cấu trúc cây đã được xác định trong INSERT tuyên bố trên. Nó có thể dễ dàng nhận thấy khi chúng ta sử dụng chuỗi. Bên cạnh đó, SQL Server chuyển đổi nó thành các giá trị thập lục phân tương ứng.

Sử dụng Max (), GetAncestor () và GetDescendant ()

Sử dụng chuỗi phù hợp với nhiệm vụ điền dữ liệu ban đầu. Về lâu dài, bạn cần mã để xử lý việc chèn mà không cần cung cấp chuỗi.

Để thực hiện tác vụ này, hãy lấy nút cuối cùng được sử dụng bởi cha mẹ hoặc tổ tiên. Chúng tôi thực hiện điều đó bằng cách sử dụng các hàm MAX () GetAncestor () . Xem mã mẫu bên dưới:

-- add a bartender rank reporting to the Asst. F&B Manager
DECLARE @MaxNode HIERARCHYID
DECLARE @ImmediateSuperior HIERARCHYID = 0x7AD6

SELECT @MaxNode = MAX(RankNode) FROM dbo.Ranks r
WHERE r.RankNode.GetAncestor(1) = @ImmediateSuperior

INSERT INTO dbo.Ranks
([Rank], RankNode, RankLevel)
VALUES
 ('Bartender', @ImmediateSuperior.GetDescendant(@MaxNode,NULL), 
    @ImmediateSuperior.GetDescendant(@MaxNode, NULL).GetLevel())

Dưới đây là các điểm lấy từ đoạn mã trên:

  • Đầu tiên, bạn cần một biến cho nút cuối cùng và nút cấp trên trực tiếp.
  • Có thể nhận được nút cuối cùng bằng cách sử dụng MAX () chống lại RankNode cho cha mẹ được chỉ định hoặc cấp trên trực tiếp. Trong trường hợp của chúng tôi, đó là Trợ lý Giám đốc F&B với giá trị nút là 0x7AD6.
  • Tiếp theo, để đảm bảo không có con trùng lặp nào xuất hiện, hãy sử dụng @ Im InstantSuperior.GetDescendant (@MaxNode, NULL) . Giá trị trong @MaxNode là con cuối cùng. Nếu nó không phải là NULL , GetDescendant () trả về giá trị nút có thể tiếp theo.
  • Cuối cùng, GetLevel () trả về cấp của nút mới được tạo.

Dữ liệu truy vấn

Sau khi thêm các bản ghi vào bảng của chúng tôi, đã đến lúc truy vấn nó. Có sẵn 2 cách để truy vấn dữ liệu:

Truy vấn cho Hậu duệ trực tiếp

Khi tìm kiếm những nhân viên trực tiếp báo cáo với người quản lý, chúng ta cần biết hai điều:

  • Giá trị nút của người quản lý hoặc phụ huynh
  • Cấp độ của nhân viên dưới quyền của người quản lý

Đối với tác vụ này, chúng ta có thể sử dụng mã bên dưới. Đầu ra là danh sách thủy thủ đoàn dưới quyền Giám đốc khách sạn.

-- Get the list of crew directly reporting to the Hotel Director
DECLARE @Node HIERARCHYID = 0x78  -- the Hotel Director's node/hierarchyid
DECLARE @Level SMALLINT = @Node.GetLevel()

SELECT
 a.CrewName
,a.DateHired
,b.Rank
,b.RankLevel
,c.VesselName
,(SELECT Rank FROM dbo.Ranks WHERE RankNode =  b.RankNode.GetAncestor(1)) AS ReportsTo
FROM dbo.Crew a
INNER JOIN dbo.Ranks b ON a.RankId = b.RankId
INNER JOIN dbo.Vessel c ON a.VesselId = c.VesselId
WHERE b.RankNode.IsDescendantOf(@Node)=1
AND b.RankLevel = @Level + 1  -- add 1 for the level of the crew under the 
                              -- Hotel Director

Kết quả của đoạn mã trên như sau trong Hình 5:

Truy vấn các Subtrees

Đôi khi, bạn cũng cần liệt kê những đứa trẻ và con của những đứa trẻ xuống dưới cùng. Để làm điều này, bạn cần có ID cấu trúc phân cấp của cấp độ gốc.

Truy vấn sẽ tương tự như mã trước đó nhưng không cần lấy cấp độ. Xem ví dụ về mã:

-- Get the list of the crew under the Hotel Director down to the lowest level
DECLARE @Node HIERARCHYID = 0x78 -- the Hotel Director's node/hierarchyid

SELECT
 a.CrewName
,a.DateHired
,b.Rank
,b.RankLevel
,c.VesselName
,(SELECT Rank FROM dbo.Ranks WHERE RankNode =  b.RankNode.GetAncestor(1)) AS ReportsTo
FROM dbo.Crew a
INNER JOIN dbo.Ranks b ON a.RankId = b.RankId
INNER JOIN dbo.Vessel c ON a.VesselId = c.VesselId
WHERE b.RankNode.IsDescendantOf(@Node)=1

Kết quả của đoạn mã trên:

Các nút di chuyển với SQL Server HierarchyID

Một thao tác tiêu chuẩn khác với dữ liệu phân cấp là chuyển một con hoặc toàn bộ cây con sang một cây mẹ khác. Tuy nhiên, trước khi chúng tôi tiếp tục, vui lòng lưu ý một vấn đề tiềm ẩn:

Sự cố tiềm ẩn

  • Đầu tiên, các nút di chuyển liên quan đến I / O. Tần suất bạn di chuyển các nút có thể là yếu tố quyết định nếu bạn sử dụng hierarchyID hoặc cha / con thông thường.
  • Thứ hai, việc di chuyển một nút trong thiết kế mẹ / con sẽ cập nhật một hàng. Đồng thời, khi bạn di chuyển một nút với hierarchyID, nó sẽ cập nhật một hoặc nhiều hàng. Số lượng hàng bị ảnh hưởng phụ thuộc vào độ sâu mức phân cấp. Nó có thể trở thành một vấn đề nghiêm trọng về hiệu suất.

Giải pháp

Bạn có thể xử lý vấn đề này với thiết kế cơ sở dữ liệu của mình.

Hãy xem xét thiết kế mà chúng tôi đã sử dụng ở đây.

Thay vì xác định thứ bậc trên Phi hành đoàn , chúng tôi đã xác định nó trong Xếp hạng bàn. Cách tiếp cận này khác với Nhân viên trong AdventureWorks cơ sở dữ liệu mẫu và nó cung cấp những ưu điểm sau:

  • Các thành viên thủy thủ đoàn di chuyển thường xuyên hơn cấp bậc trên tàu. Thiết kế này sẽ giảm chuyển động của các nút trong hệ thống phân cấp. Do đó, nó giảm thiểu sự cố được xác định ở trên.
  • Xác định nhiều thứ bậc trong Phi hành đoàn bảng phức tạp hơn, vì hai tàu cần hai thuyền trưởng. Kết quả là hai nút gốc.
  • Nếu bạn cần hiển thị tất cả các cấp bậc với thành viên phi hành đoàn tương ứng, bạn có thể sử dụng THAM GIA TRÁI. Nếu không có ai trong bảng xếp hạng đó, nó sẽ hiển thị một vị trí trống cho vị trí đó.

Bây giờ, hãy chuyển sang mục tiêu của phần này. Thêm nút con dưới cha mẹ sai.

Để hình dung những gì chúng ta sắp làm, hãy tưởng tượng một hệ thống phân cấp như bên dưới. Lưu ý các nút màu vàng.

Di chuyển nút không có trẻ em

Di chuyển một nút con yêu cầu những điều sau:

  • Xác định ID phân cấp của nút con cần di chuyển.
  • Xác định ID phân cấp của cha mẹ cũ.
  • Xác định ID phân cấp của cấp độ gốc mới.
  • Sử dụng UPDATE với GetReparentedValue () để di chuyển nút về mặt vật lý.

Bắt đầu bằng cách di chuyển một nút không có nút con. Trong ví dụ dưới đây, chúng tôi chuyển Nhân viên của Du thuyền từ dưới quyền Giám đốc Du thuyền sang dưới Asst. Giám đốc Du thuyền.

-- Moving a node with no child node

DECLARE @NodeToMove HIERARCHYID
DECLARE @OldParent HIERARCHYID
DECLARE @NewParent HIERARCHYID

SELECT @NodeToMove = r.RankNode
FROM dbo.Ranks r
WHERE r.RankId = 24 -- the cruise staff

SELECT @OldParent = @NodeToMove.GetAncestor(1)

SELECT @NewParent = r.RankNode
FROM dbo.Ranks r
WHERE r.RankId = 19  -- the assistant cruise director

UPDATE dbo.Ranks
SET RankNode = @NodeToMove.GetReparentedValue(@OldParent,@NewParent)
WHERE RankNode = @NodeToMove

Khi nút được cập nhật, một giá trị hex mới sẽ được sử dụng cho nút. Đang làm mới kết nối Power BI của tôi với SQL Server - nó sẽ thay đổi biểu đồ phân cấp như được hiển thị bên dưới:

Trong Hình 8, nhân viên Cruise không còn báo cáo với Giám đốc Du thuyền nữa - nó được chuyển sang báo cáo cho Trợ lý Giám đốc Du lịch. So sánh nó với Hình 7 ở trên.

Bây giờ, chúng ta hãy chuyển sang giai đoạn tiếp theo và chuyển Trưởng bộ phận phục vụ đến Trợ lý Giám đốc F&B.

Di chuyển nút với trẻ em

Có một thách thức trong phần này.

Vấn đề là, mã trước đó sẽ không hoạt động với một nút chỉ có một nút con. Chúng tôi nhớ rằng việc di chuyển một nút yêu cầu phải cập nhật một hoặc nhiều nút con.

Hơn nữa, nó không kết thúc ở đó. Nếu cấp độ gốc mới có con hiện tại, chúng ta có thể gặp phải các giá trị nút trùng lặp.

Trong ví dụ này, chúng ta phải đối mặt với vấn đề đó:Asst. F&B Manager có nút con Bartender.

Sẵn sàng? Đây là mã:

-- Move a node with at least one child
DECLARE @NodeToMove HIERARCHYID
DECLARE @OldParent HIERARCHYID
DECLARE @NewParent HIERARCHYID

SELECT @NodeToMove = r.RankNode
FROM dbo.Ranks r
WHERE r.RankId = 22 -- the head waiter

SELECT @OldParent = @NodeToMove.GetAncestor(1) -- head waiter's old parent 
                                               --> asst chief housekeeping

SELECT @NewParent = r.RankNode
FROM dbo.Ranks r
WHERE r.RankId = 14  -- the assistant f&b manager

DECLARE children_cursor CURSOR FOR 
  SELECT RankNode FROM dbo.Ranks r
  WHERE RankNode.GetAncestor(1) = @OldParent; 

  DECLARE @ChildId hierarchyid; 
  OPEN children_cursor 

  FETCH NEXT FROM children_cursor INTO @ChildId; 
  WHILE @@FETCH_STATUS = 0 
  BEGIN 
    START: 
    DECLARE @NewId hierarchyid; 

    SELECT @NewId = @NewParent.GetDescendant(MAX(RankNode), NULL) 
    FROM dbo.Ranks r WHERE RankNode.GetAncestor(1) = @NewParent;  -- ensure 
                                       --to get a new id in case there's a 
                                       --sibling

    UPDATE dbo.Ranks 
    SET RankNode = RankNode.GetReparentedValue(@ChildId, @NewId) 
    WHERE RankNode.IsDescendantOf(@ChildId) = 1; 
   
    IF @@error <> 0 GOTO START -- On error, retry 
    FETCH NEXT FROM children_cursor INTO @ChildId; 
  END 
CLOSE children_cursor; 
DEALLOCATE children_cursor;

Trong ví dụ mã trên, quá trình lặp bắt đầu khi cần chuyển nút xuống con ở cấp cuối cùng.

Sau khi bạn chạy nó, Xếp hạng bảng sẽ được cập nhật. Và một lần nữa, nếu bạn muốn xem các thay đổi một cách trực quan, hãy làm mới báo cáo Power BI. Bạn sẽ thấy những thay đổi tương tự như bên dưới:

Lợi ích của việc sử dụng SQL Server HierarchyID so với Parent / Child

Để thuyết phục bất kỳ ai sử dụng một tính năng, chúng ta cần biết những lợi ích.

Vì vậy, trong phần này, chúng ta sẽ so sánh các câu lệnh sử dụng cùng một bảng giống như ở phần đầu. Một sẽ sử dụng hierarchyID và một sẽ sử dụng phương pháp cha / con. Tập kết quả sẽ giống nhau cho cả hai cách tiếp cận. Chúng tôi mong đợi nó cho bài tập này giống như bài tập từ Hình 6 ở trên.

Bây giờ các yêu cầu đã chính xác, chúng ta hãy kiểm tra các lợi ích một cách kỹ lưỡng.

Đơn giản hơn để viết mã

Xem mã bên dưới:

-- List down all the crew under the Hotel Director using hierarchyID
SELECT
a.CrewName
,a.DateHired
,b.Rank
,b.RankLevel
,c.VesselName
,d.RANK AS ReportsTo
FROM dbo.Crew a
INNER JOIN dbo.Vessel c ON a.VesselId = c.VesselId
INNER JOIN dbo.Ranks b ON a.RankId = b.RankId
INNER JOIN dbo.Ranks d ON d.RankNode = b.RankNode.GetAncestor(1)
WHERE a.VesselId = 1
AND b.RankNode.IsDescendantOf(0x78)=1

Mẫu này chỉ cần một giá trị ID cấu trúc phân cấp. Bạn có thể thay đổi giá trị theo ý muốn mà không cần thay đổi câu truy vấn.

Bây giờ, hãy so sánh câu lệnh cho phương pháp cha / con tạo ra cùng một tập kết quả:

-- List down all the crew under the Hotel Director using parent/child
SELECT
a.CrewName
,a.DateHired
,b.Rank
,b.RankLevel
,c.VesselName
,d.Rank AS ReportsTo
FROM dbo.Crew a
INNER JOIN dbo.Vessel c ON a.VesselId = c.VesselId
INNER JOIN dbo.Ranks b ON a.RankId = b.RankId
INNER JOIN dbo.Ranks d ON b.RankParentId = d.RankId
WHERE a.VesselId = 1
AND (b.RankID = 4) OR (b.RankParentID = 4 OR b.RankParentId >= 7)

Bạn nghĩ sao? Các mẫu mã gần như giống nhau trừ một điểm.

WHERE mệnh đề trong truy vấn thứ hai sẽ không linh hoạt để điều chỉnh nếu cần một cây con khác.

Làm cho truy vấn thứ hai đủ chung chung và mã sẽ dài hơn. Rất tiếc!

Thực thi nhanh hơn

Theo Microsoft, "truy vấn cây con nhanh hơn đáng kể với hierarchyID" so với cha / con. Hãy xem điều đó có đúng không.

Chúng tôi sử dụng các truy vấn tương tự như trước đó. Một số liệu quan trọng để sử dụng cho hiệu suất là số lần đọc hợp lý từ ĐẶT THỐNG KÊ IO . Nó cho biết SQL Server sẽ cần bao nhiêu trang 8KB để có được tập kết quả mà chúng ta muốn. Giá trị càng cao, số lượng trang SQL Server truy cập và đọc càng lớn và truy vấn chạy càng chậm. Thực thi ĐẶT BẬT THỐNG KÊ và thực hiện lại hai truy vấn trên. Giá trị thấp hơn của các lần đọc hợp lý sẽ là người chiến thắng.

PHÂN TÍCH

Như bạn có thể thấy trong Hình 10, thống kê I / O cho truy vấn với hierarchyID có số lần đọc logic thấp hơn so với các đối tác cha / con của chúng. Lưu ý những điểm sau trong kết quả này:

  • Tàu bảng đáng chú ý nhất trong ba bảng. Sử dụng hierarchyID chỉ yêu cầu 2 * 8KB =16KB trang được SQL Server đọc từ bộ nhớ đệm (bộ nhớ). Trong khi đó, việc sử dụng cha / con yêu cầu 26 * 8KB =208KB trang - cao hơn đáng kể so với sử dụng hierarchyID.
  • Xếp hạng bảng, bao gồm định nghĩa của chúng tôi về cấu trúc phân cấp, yêu cầu 92 * 8KB =736KB. Mặt khác, sử dụng cha / con yêu cầu 132 * 8KB =1056KB.
  • Phi hành đoàn bảng cần 2 * 8KB =16KB, điều này giống nhau cho cả hai cách tiếp cận.

Kilobyte trang có thể là một giá trị nhỏ vào lúc này, nhưng chúng tôi chỉ có một vài bản ghi. Tuy nhiên, nó cung cấp cho chúng tôi ý tưởng về cách đánh thuế truy vấn của chúng tôi trên bất kỳ máy chủ nào. Để cải thiện hiệu suất, bạn có thể thực hiện một hoặc nhiều hành động sau:

  • Thêm (các) chỉ mục thích hợp
  • Cấu trúc lại truy vấn
  • Cập nhật số liệu thống kê

Nếu bạn đã làm như trên và số lần đọc logic giảm đi mà không cần thêm nhiều bản ghi, thì hiệu suất sẽ tăng lên. Miễn là bạn làm cho số đọc logic thấp hơn so với số đọc sử dụng hierarchyID, đó sẽ là tin tốt.

Nhưng tại sao lại đề cập đến số lần đọc logic thay vì thời gian đã trôi qua?

Kiểm tra thời gian đã trôi qua cho cả hai truy vấn bằng cách sử dụng BẬT THỜI GIAN THỐNG KÊ tiết lộ một số lượng nhỏ khác biệt mili giây đối với tập dữ liệu nhỏ của chúng tôi. Ngoài ra, máy chủ phát triển của bạn có thể có cấu hình phần cứng, cài đặt SQL Server và khối lượng công việc khác. Thời gian trôi qua dưới một phần nghìn giây có thể đánh lừa bạn xem truy vấn của bạn có hoạt động nhanh như bạn mong đợi hay không.

ĐƯỜNG LỐI THÊM

ĐẶT BẬT THỐNG KÊ không tiết lộ những điều đang xảy ra "đằng sau hậu trường." Trong phần này, chúng ta tìm hiểu lý do tại sao SQL Server xuất hiện với những con số đó bằng cách xem xét kế hoạch thực thi.

Hãy bắt đầu với kế hoạch thực thi của truy vấn đầu tiên.

Bây giờ, hãy xem kế hoạch thực thi của truy vấn thứ hai.

So sánh Hình 11 và Hình 12, chúng ta thấy rằng SQL Server cần thêm nỗ lực để tạo ra tập kết quả nếu bạn sử dụng phương pháp cha / con. WHERE điều khoản chịu trách nhiệm cho sự phức tạp này.

Tuy nhiên, lỗi cũng có thể là do thiết kế của bàn. Chúng tôi đã sử dụng cùng một bảng cho cả hai phương pháp: Xếp hạng bàn. Vì vậy, tôi đã cố gắng sao chép Xếp hạng bảng nhưng sử dụng các chỉ mục được phân cụm khác nhau thích hợp cho từng quy trình.

Kết quả là, sử dụng hierarchyID vẫn có ít lần đọc logic hơn so với đối tác cha / con. Cuối cùng, chúng tôi đã chứng minh rằng Microsoft đã đúng khi tuyên bố điều đó.

Kết luận

Đây là thời điểm trung tâm cho hierarchyID là:

  • HierarchyID là kiểu dữ liệu tích hợp được thiết kế để biểu diễn cây được tối ưu hóa hơn, đây là kiểu dữ liệu phân cấp phổ biến nhất.
  • Mỗi mục trong cây là một nút và các giá trị ID phân cấp có thể ở định dạng thập lục phân hoặc chuỗi.
  • HierarchyID có thể áp dụng cho dữ liệu về cấu trúc tổ chức, nhiệm vụ của dự án, dữ liệu địa lý và những thứ tương tự.
  • Có các phương pháp để duyệt và xử lý dữ liệu phân cấp, chẳng hạn như GetAncestor (), GetDescendant (). GetLevel (), GetReparentedValue () và hơn thế nữa.
  • Cách thông thường để truy vấn dữ liệu phân cấp là lấy các con trực tiếp của một nút hoặc lấy các cây con dưới một nút.
  • Việc sử dụng hierarchyID để truy vấn các cây con không chỉ đơn giản hơn để viết mã. Nó cũng hoạt động tốt hơn cha / con.

Thiết kế dành cho phụ huynh / trẻ em không tệ chút nào, và bài đăng này không làm giảm nó. Tuy nhiên, việc mở rộng các tùy chọn và giới thiệu các ý tưởng mới luôn mang lại lợi ích lớn cho nhà phát triển.

Bạn có thể tự mình thử các ví dụ mà chúng tôi cung cấp ở đây. Nhận các hiệu ứng và xem cách bạn có thể áp dụng nó cho dự án tiếp theo của mình liên quan đến hệ thống phân cấp.

Nếu bạn thích bài đăng và ý tưởng của nó, bạn có thể quảng cáo bằng cách nhấp vào các nút chia sẻ cho phương tiện truyền thông xã hội ưa thích.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Tôi có thể bật ignore_dup_key cho khóa chính không?

  2. Tại sao chúng ta luôn thích sử dụng các tham số trong các câu lệnh SQL?

  3. Cơ sở dữ liệu là gì, Tại sao lại là Cơ sở dữ liệu?

  4. CAST và IsNumeric

  5. 4 Hoạt động giám sát cơ sở dữ liệu chính mà mọi DBA nên biết