Hierarchies in SQL Server 2008

最近在做一个silverlight项目,有关于树的,需要用到SQL Server 2008里的Hierarchies ,以下文章是转载自国外的一个博客,自感觉挺不错的:

Graphs, tree algorithms and structures have been used for long time in databases to solve hierarchy related problems. Adjacency list, nested sets, materialized path, and other hybrid methods offer different capabilities to help.

SQL Server 2008 adds a new feature to help with modeling hierarchical relationships: the HIERARCHYID data type. It provides compact storage and convenient methods to manipulate hierarchies. In a way it is very much like optimized materialized path. In addition the SqlHierarchyId CLR data type is available for client applications.

While HIERARCHYID has a lot to offer in terms of operations with hierarchical data, it is important to understand a few basic concepts:

- HIERARCHYID can have only a single root (although easy to work around by adding sub-roots)
- It does not automatically represent a tree, the application has to define the relationships and enforce all rules
- The application needs to maintain the consistency

Here is a one example of employee hierarchy to illustrate the usage of HIERARCHYID and the related methods for manipulation of hierarchies.

  
  

CREATE TABLE






Employees (








 emp_id INT






NOT NULL






PRIMARY KEY






,








 emp_name VARCHAR






(35),








 manager_id INT REFERENCES






Employees(emp_id),








 org_chart_path HIERARCHYID






);








 








/*








 








The primary key prevents cyclic paths.








Another way is using a CHECK constraint.








 








CHECK (org_chart_path.ToString() NOT LIKE '%/' + CAST(emp_id AS VARCHAR(10)) + '/_%')








 








*/








 








-- Insert the top level manager as hierarchy root








INSERT INTO






Employees








VALUES






(1, 'Jeff Brown'






, NULL






, hierarchyid






::GetRoot());








 








-- Insert John who reports to the top level manager








INSERT INTO






Employees








VALUES






(2, 'John Doe'






, 1,








           (SELECT






hierarchyid






::GetRoot().GetDescendant(NULL






, NULL






)








            FROM






Employees));








 








 








-- Insert Peter at the same level as John








DECLARE






@mgr HIERARCHYID






= (SELECT






org_chart_path








                            FROM






Employees








                            WHERE






emp_name = 'John Doe'






);








 








INSERT INTO






Employees








VALUES






(3, 'Peter Hanna'






, 1,








           (SELECT






hierarchyid






::GetRoot().GetDescendant(@mgr, NULL






)








            FROM






Employees








            WHERE






org_chart_path = hierarchyid






::GetRoot()));








 








-- Insert Richard as reporting to John








INSERT INTO






Employees








VALUES






(4, 'Richard Burns'






, 2,








           hierarchyid






::Parse('/1/1/'






)); -- Also: CAST('/1/1/' AS HIERARCHYID)








 








SELECT






emp_id, emp_name,








       manager_id, org_chart_path,








       org_chart_path.GetAncestor(1) AS






emp_manager,








       hierarchyid






::GetRoot() AS






top_manager,








       org_chart_path.GetDescendant(NULL






, NULL






) AS






emp_descendant,








       org_chart_path.GetLevel() AS






emp_level,








       org_chart_path.ToString() AS






emp_org_path








FROM






Employees;








 








/*








 








emp_id emp_name






        manager_id






org_chart_path






emp_manager






  top_manager






emp_descendant






emp_level emp_org_path








------ ---------------- ----------- --------------- ------------- ------------ --------------- --------- ------------








1






    Jeff Brown






      NULL






       0x






            NULL






        0x






          0x58






           0






        /








2






    John Doe






        1






          0x58






           0x






           0x






          0x5AC0






        1






        /1/








3






    Peter Hanna






    1






          0x68






           0x






           0x






          0x6AC0






        1






        /2/








4






    Richard Burns






   2






          0x5AC0






        0x58






        0x






          0x5AD6






        2






        /1/1/








 








*/








 








-- Move Richard to report to Peter








DECLARE






@new_mgr HIERARCHYID






= (SELECT






org_chart_path








                                FROM






Employees








                                WHERE






emp_name = 'Peter Hanna'






);








 








UPDATE






Employees








SET






org_chart_path = org_chart_path.Reparent(org_chart_path.GetAncestor(1),








                                            @new_mgr)








WHERE






emp_name = 'Richard Burns'






;








 








SELECT






emp_id, emp_name,








       manager_id, org_chart_path,








       org_chart_path.GetAncestor(1) AS






emp_manager,








       hierarchyid






::GetRoot() AS






top_manager,








       org_chart_path.GetDescendant(NULL






, NULL






) AS






emp_descendant,








       org_chart_path.GetLevel() AS






emp_level,








       org_chart_path.ToString() AS






emp_org_path








FROM






Employees;   








 








/*








 








emp_id






emp_name






       manager_id






org_chart_path






   emp_manager






  top_manager






emp_descendant






  emp_level emp_org_path








------- --------------- ----------- ----------------- ------------- ------------ ---------------- --------- ------------








1






      Jeff Brown






    NULL






       0x






               NULL






        0x






          0x58






            0






        /








2






      John Doe






       1






          0x58






            0x






           0x






          0x5AC0






          1






        /1/








3






      Peter Hanna






    1






          0x68






            0x






           0x






          0x6AC0






          1






        /2/








4






      Richard Burns






  2






          0x6AC0






           0x68






        0x






          0x6AD6






          2






        /2/1/








 








*/

















From the above example it is very easy to see the similarity between materialized path and HIERARCHYID when the HIERARCHYID is converted to the character format using the ToString() method. Converting hierarchy from traditional parent/child format to HIERARCHYID is simple using recursive CTEs (very similar to building a materialized path).

Note:

This code has been tested with SQL Server 2008 CTP 6 (February 2008). As of SQL Server 2008 Release Candidate 0 (June 2008) the method Reparent() has been replaced with the method GetReparentedValue(). It is called using the same parameters and returns the same value.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值