Hierarchies in SQL Server 2008
2011-05-04 14:57
309 查看
最近在做一个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_p
dde9
ath,
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.
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_p
dde9
ath,
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.
相关文章推荐
- Hierarchies in SQL Server 2008(2)
- [转]Database Mirroring Log Compression in SQL Server 2008 Improves Throughput
- Microsoft SQL Server 2008 All-in-One Desk Reference For Dummies 读书笔记之三
- Enabling Dedicated Administrator Connection Feature in SQL Server 2008
- 视图 调用 存储过程 view call procedure in sqlserver 2008
- TFS Project error in SQL Server 2005 after installing SQL Server 2008
- 在SQL Server2008中使用(数据)更新跟踪功能-Using Change Tracking in SQL Server 2008
- Configure Windows Firewall for SQL Server 2008 Analysis Services in Windows Server 2008 R2
- Microsoft SQL Server 2008 All-in-One Desk Reference For Dummies
- What’s New in SQL Server 2008
- FILESTREAM Configuration and Setup Changes in SQL Server 2008 February CTP
- Create maintenance backup plan in SQL Server 2008 R2 using the wizard
- Summary of Useful Features in SQL Server 2008
- Error message when you try to save a table in SQL Server 2008: "Saving changes is not permitted"
- Enable CLR in SQL Server 2005/2008
- Versatile High Performance Hierarchies in SQL Server
- 创建Linking Server in SQL SERVER 2008
- ?? [SQL Server] -- Using FILESTREAM to Store BLOBs in the NTFS File System in SQL Server 2008
- Utility Control Points in SQL Server 2008 R2
- Problem in connecting VS 2005 to SQL Server 2008