锋利的SQL2014:层次结构操作之Hierarchyid(三)

2015-01-24 01:41:36 · 作者: · 浏览: 8
d); --目标结点

UPDATE dbo.Employees

SET Org_Node =Org_Node.GetReparentedValue(@oldRoot, @newRoot)

WHEREOrg_Node.IsDescendantOf(@oldRoot) = 1;

执行下面的语句,查询结果如表19-33所示。

SELECT *,Org_Node.ToString() AS Org_Node_Str

FROM dbo.Employees;

表19-33 将部门经理A移动到副总经理B下面

Org_Node

EmployeeId

Title

Org_Level

Org_Node_Str

0x

1

总经理

0

/

0x58

2

副总经理A

1

/1/

0x68

3

副总经理B

1

/2/

0x6AC0

4

部门经理A

2

/2/1/

0x5B40

5

部门经理B

2

/1/2/

0x5AD6

6

员工A

3

/2/1/1/

0x5B56

7

员工B

3

/1/2/1/

0x5B5A

8

员工C

3

/1/2/2/

0x6AC0

9

部门经理C

2

/2/1/

0x5B58B0

10

员工D

3

/1/2/1.1/

可以看到,部门经理A的节点变成了“/2/1/”,成功地变更到副总经理B下面。但是,这里存在一个问题,就是部门经理A与部门经理C的hierarchyid是相同的,出现了重复值,hierarchyid数据类型虽然用于表示层次结构,但它并不强制实现层次结构,层次的准确性由用户自己掌控。当然,可以通过为hierarchyid类型列建立唯一索引的方式避免出现重复条目。当然,出现此问题的原因是我们没有判断副总经理B下面层级中的最大hierarchyid值,错误地指定了一个已存在hierarchyid。下面来看一下正确的解决方法。

首先使用下面的语句恢复数据的原状。

UPDATE dbo.Employees

SET Org_Node = '/1/1/'

WHERE EmployeeId = 4;

UPDATE dbo.Employees

SET Org_Node = '/1/1/1/'

WHERE EmployeeId = 6;

下面的语句创建了一个存储过程,可以实现节点的变更。该过程接受两个参数,一个是需要调整雇员的D,另一个是其目标管理人员的ID。注意第三条SELECT语句中MAX函数和GetAncestor方法的使用,该语句的作用是在目标管理人员下一层级最大hierarchyid值的基础上获得一个新的hierarchyid值。

CREATE PROCEDUREMoveOrg(@EmpID int, @newMgrID int )

AS

BEGIN

DECLARE @nold hierarchyid, @nnew hierarchyid

SELECT @nold = Org_Node FROM dbo.EmployeesWHERE EmployeeId = @EmpID ;

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE

BEGIN TRANSACTION

SELECT @nnew = Org_Node FROM dbo.EmployeesWHERE EmployeeId = @newMgrID ;

SELECT @nnew = @nnew.GetDescendant(MAX(Org_Node), NULL)

FROM dbo.Employees WHERE Org_Node.GetAncestor(1)=@nnew ;

UPDATE dbo.Employees

SET Org_Node =Org_Node.GetReparentedValue(@nold, @nnew)

WHERE Org_Node.IsDescendantOf(@nold) = 1 ;

COMMIT TRANSACTION

END ;

执行存储过程,可以看到部门经理A成功地调整到了副总经理B下面,并且部门经理A的hierarchyid值是在层级最大值“/2/1/”的基础上递增获得“/2/2/”。查询结果如表19-34所示。

EXECUTE dbo.MoveOrg 4, 3; --执行存储过程

SELECT *,Org_Node.ToString() AS Org_Node_Str

FROM dbo.Employees;

表19-34 使用存储过程将部门经理A移动到副总经理B下面

Org_Node

EmployeeId

Title

Org_Level

Org_Node_Str

0x

1

总经理

0

/

0x58

2

副总经理A

1

/1/

0x68

3

副总经理B

1

/2/

0x6B40

4

部门经理A

2

/2/2/

0x5B40

5

部门经理B

2

/1/2/

0x6B56

6

员工A

3

/2/2/1/

0x5B56

7

员工B

3

/1/2/1/

0x5B5A

8

员工C

3

/1/2/2/

0x6AC0

9

部门经理C

2

/2/1/

0x5B58B0

10

员工D

3

/1/2/1.1/

19.8.5 hierarchyid的索引策略

用于对分层数据进行索引的策略有两种:深度优先和广度优先。深度优先索引,子树中各行的存储位置相邻,简而言之,就是以hierarchyid值排序的方式存储,如图19-2所示。对于本节的示例而言,就是经理管理的所有雇员都存储在其经理的记录附近。

\

图19-2 深度优先索引策略

下面的语句基于Org_Node列,创建了深度优先索引。

CREATE UNIQUE INDEXOrgNode_Depth_First

ON dbo.Employees(Org_Node);

下面的查询语句按Org_Node排序输出,注意其中的Org_Node_Str列,这就是深度优先的索引存储方式。如表19-35所示。<??http://www.2cto.com/kf/ware/vc/" target="_blank" class="keylink">vcD48cD5TRUxFQ1QgKixPcmdfTm9kZS5Ub1N0cmluZygpIEFTIE9yZ19Ob2RlX1N0ciA8L3A+PHA+RlJPTSBkYm8uRW1wbG95ZWVzIDwvcD48cD5PUkRFUiBCWSBPcmdfTm9kZTs8L3A+PHA+se0xOS0zNSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgye62yNPFz8i1xMv30v205rSit73KvTwvcD48dGFibGUgYm9yZGVyPQ=="1" cellspacing="0" cellpadding="0" width="100%">

Org_Node

EmployeeId

Title

Org_Level

Org_Node_Str

0x

1

总经理

0

/

0x58

2

副总经理A

1

/1/

0x5B40

5

部门经理B

2

/1/2/

0x5B56

7

员工B

3

/1/2/1/

0x5B58B0

10

员工D

3

/1/2/1.1/

0x5B5A

8

员工C

3

/1/2/2/

0x68

3

副总经理B

1

/2/

0x6AC0

9

部门经理C

2

/2/1/

0x6B40

4

部门经理A

2

/2/2/

0x6B56

6

员工A

3

/2/2/1/

广度优先索引,是将层次结构中每个级别的各行存储在一起,简而言之,就是按层级排序的方式存储,如图19-3所示。对于本节的示例而言,同一经理直属的各雇员的记录存储在相邻位置。

\

图19-3 广度优先索引策略

下面的语句基于Org_Node列,创建了广度优先索引。

CREATE CLUSTERED INDEXOrgNode_Breadth_First

ONdbo.Employees(Org_Level);

下面的查询语句按Org_Level排序输出,注意其中的Org_Level列,这就是广度优先的索引存储方式。如表19-36所示。

SELECT *,Org_Node.ToString() AS Org_Node_Str

FROM dbo.Employees

ORDER BY Org_Level;

表19-36 广度优先的索引存储方式

Org_Node

EmployeeId

Title

Org_Level

Org_Node_Str

0x

1

总经理

0

/

0x58

2

副总经理A

1

/1/

0x68

3

副总经理B

1

/2/

0x6B40

4

部门经理A

2

/2/2/

0x5B40

5

部门经理B

2

/1/2/

0x6AC0

9

部门经理C

2

/2/1/

0x5B58B0

10

员工D

3

/1/2/1.1/

0x6B56

6

员工A

3

/2/2/1/

0x5B5A

8

员工C

3

/1/2/2/

0x5B56

7

员工B

3

/1/2/1/