您的位置:首页 > 数据库

sql选出层级树(转)

2015-09-25 14:25 316 查看

Posted on 2012-12-08 09:54
cunriqian 阅读(387) 评论(0)

编辑
收藏

在RDBMS中操作 图 树 层次结构 等特殊的数据结构时,我们通常采用2个主要方法:

1.基于迭代/递归

2.具体化描述数据结构的附加信息

一般模型有:员工组织图(树,层次结构);料表--BOM(有向图);道路系统(无向循环图)

1.迭代/递归

迭代可以迭代图的一个节点,也可以迭代一个层次.后者比前者要快很多.

实现方法:SQL2000通过UDF(用户自定义函数),SQL2005使用CTE

a.下属问题(通俗说,求子节点)

--这里我使用书上的员工表(表内容如下)

[c-sharp] view plaincopyprint?

SET NOCOUNT ON;
USE tempdb;
GO
IF OBJECT_ID('dbo.Employees') IS NOT NULL
DROP TABLE dbo.Employees;
GO
CREATE TABLE dbo.Employees
(
empid INT NOT NULL PRIMARY KEY,
mgrid INT NULL REFERENCES dbo.Employees,
empname VARCHAR(25) NOT NULL,
salary MONEY NOT NULL,
CHECK (empid <> mgrid)
);
INSERT INTO dbo.Employees(empid, mgrid, empname, salary)
VALUES(1, NULL, 'David', $10000.00);
INSERT INTO dbo.Employees(empid, mgrid, empname, salary)
VALUES(2, 1, 'Eitan', $7000.00);
INSERT INTO dbo.Employees(empid, mgrid, empname, salary)
VALUES(3, 1, 'Ina', $7500.00);
INSERT INTO dbo.Employees(empid, mgrid, empname, salary)
VALUES(4, 2, 'Seraph', $5000.00);
INSERT INTO dbo.Employees(empid, mgrid, empname, salary)
VALUES(5, 2, 'Jiru', $5500.00);
INSERT INTO dbo.Employees(empid, mgrid, empname, salary)
VALUES(6, 2, 'Steve', $4500.00);
INSERT INTO dbo.Employees(empid, mgrid, empname, salary)
VALUES(7, 3, 'Aaron', $5000.00);
INSERT INTO dbo.Employees(empid, mgrid, empname, salary)
VALUES(8, 5, 'Lilach', $3500.00);
INSERT INTO dbo.Employees(empid, mgrid, empname, salary)
VALUES(9, 7, 'Rita', $3000.00);
INSERT INTO dbo.Employees(empid, mgrid, empname, salary)
VALUES(10, 5, 'Sean', $3000.00);
INSERT INTO dbo.Employees(empid, mgrid, empname, salary)
VALUES(11, 7, 'Gabriel', $3000.00);
INSERT INTO dbo.Employees(empid, mgrid, empname, salary)
VALUES(12, 9, 'Emilia' , $2000.00);
INSERT INTO dbo.Employees(empid, mgrid, empname, salary)
VALUES(13, 9, 'Michael', $2000.00);
INSERT INTO dbo.Employees(empid, mgrid, empname, salary)
VALUES(14, 9, 'Didi', $1500.00);
--创建索引
CREATE UNIQUE INDEX idx_unc_mgrid_empid ON dbo.Employees(mgrid, empid);
go

--SQL2000 udf方法:

[b][b][css] view plaincopyprint?[/b][/b]

IF OBJECT_ID('dbo.fn_subordinates1') IS NOT NULL
DROP FUNCTION dbo.fn_subordinates1;
GO
CREATE FUNCTION dbo.fn_subordinates1(@root AS INT)
RETURNS @Subs TABLE
(
empid INT NOT NULL PRIMARY KEY NONCLUSTERED,
lvl INT NOT NULL,
UNIQUE CLUSTERED(lvl, empid)
)
AS
begin
declare @lv int
set @lv=0
insert @Subs values(@root,@lv)
while @@rowcount>0
begin
set @lv=@Lv+1;
insert @subs
select b.empid ,@Lv
from @subs a join dbo.Employees b on a.empid=b.mgrid and lvl=@lv-1
end
return;
end
go
SELECT empid, lvl FROM dbo.fn_subordinates1(3) AS S;

--SQL2005 CTE

[b][b][c-sharp] view plaincopyprint?[/b][/b]

DECLARE @root AS INT;
SET @root = 3;
WITH SubsCTE
AS
(
-- Anchor member returns root node
SELECT empid, empname, 0 AS lvl
FROM dbo.Employees
WHERE empid = @root

UNION ALL

-- Recursive member returns next level of children
SELECT C.empid, C.empname, P.lvl + 1
FROM SubsCTE AS P
JOIN dbo.Employees AS C
ON C.mgrid = P.empid
)
SELECT * FROM SubsCTE;

--查询结果

[c-sharp] view plaincopyprint?

/*
empid empname lvl
----------- ------------------------- -----------
3 Ina 0
7 Aaron 1
9 Rita 2
11 Gabriel 2
12 Emilia 3
13 Michael 3
14 Didi 3

*/

---------------如果需要限制递归的层数------------

--SQL2000

[c-sharp] view plaincopyprint?

IF OBJECT_ID('dbo.fn_subordinates2') IS NOT NULL
DROP FUNCTION dbo.fn_subordinates2;
GO
CREATE FUNCTION dbo.fn_subordinates2
(@root AS INT, @maxlevels AS INT = NULL) RETURNS @Subs TABLE
(
empid INT NOT NULL PRIMARY KEY NONCLUSTERED,
lvl INT NOT NULL,
UNIQUE CLUSTERED(lvl, empid)
)
AS
BEGIN
DECLARE @lvl AS INT;
SET @lvl = 0;
-- 如果@maxlevels输入的是NULL,把他变为最大的INT类型
SET @maxlevels = COALESCE(@maxlevels, 2147483647);
INSERT INTO @Subs(empid, lvl)
SELECT empid, @lvl FROM dbo.Employees WHERE empid = @root;

WHILE @@rowcount > 0
AND @lvl < @maxlevels -- 这里注意加一个小于最大的递归数的条件,是小于 不是小于等于
BEGIN
SET @lvl = @lvl + 1;

INSERT INTO @Subs(empid, lvl)
SELECT C.empid, @lvl --这里跟上面处理都一样
FROM @Subs AS P
JOIN dbo.Employees AS C
ON P.lvl = @lvl - 1
AND C.mgrid = P.empid;
END

RETURN;
END
GO
SELECT empid, lvl
FROM dbo.fn_subordinates2(3, NULL) AS S;

--查询结果

[c-sharp] view plaincopyprint?

/*
empid lvl
----------- -----------
3 0
7 1
9 2
11 2
12 3
13 3
14 3
*/
-----

SELECT empid, lvl

FROM dbo.fn_subordinates2(2, NULL) AS S;



[c-sharp] view plaincopyprint?

/*
empid lvl
----------- -----------
2 0
4 1
5 1
6 1
8 2
10 2
*/

PS:这里控制返回的层数 你当然可以用最开始的方法,然后再筛选语句里控制层数

如SELECT empid, lvl FROM dbo.fn_subordinates1(3) AS S where lvl<3; 但是控制本身的递归只能在UDF里面了

--sql2005方法类似

[c-sharp] view plaincopyprint?

WITH SubsCTE
AS
(
SELECT empid, empname, 0 AS lvl
FROM dbo.Employees
WHERE empid = @root
UNION ALL

SELECT C.empid, C.empname, P.lvl + 1
FROM SubsCTE AS P
JOIN dbo.Employees AS C
ON C.mgrid = P.empid
AND P.lvl < @maxlevels -- 这里控制递归数
)
SELECT * FROM SubsCTE;

--还有一种偏方:但是不推荐 虽然结果正确 但是会报错

[c-sharp] view plaincopyprint?

WITH SubsCTE
AS
(
SELECT empid, empname, 0 AS lvl
FROM dbo.Employees
WHERE empid = @root

UNION ALL

SELECT C.empid, C.empname, P.lvl + 1
FROM SubsCTE AS P
JOIN dbo.Employees AS C
ON C.mgrid = P.empid
)
SELECT * FROM SubsCTE
OPTION (MAXRECURSION 2);--就是这里的MAXRECURSION 控制递归

--查询结果

[c-sharp] view plaincopyprint?

/*
empid empname lvl
----------- ------------------------- -----------
3 Ina 0
7 Aaron 1
9 Rita 2
11 Gabriel 2
消息 530,级别 16,状态 1,第 4 行
语句被终止。完成执行语句前已用完最大递归 2。
*/

b.祖先(通俗说:求父节点)

其实思路跟子节点差不多

--SQL2000

[c-sharp] view plaincopyprint?

IF OBJECT_ID('dbo.fn_managers') IS NOT NULL
DROP FUNCTION dbo.fn_managers;
GO
CREATE FUNCTION dbo.fn_managers
(@empid AS INT, @maxlevels AS INT = NULL) RETURNS @Mgrs TABLE
(
empid INT NOT NULL PRIMARY KEY,
lvl INT NOT NULL
)
AS
BEGIN
IF NOT EXISTS(SELECT * FROM dbo.Employees WHERE empid = @empid) --这里判断是否存在经理,不存在 马上返回
RETURN;

DECLARE @lvl AS INT,@mgrid int ;
SET @lvl = 0;
set @mgrid=@empid --赋值给@mgrid 我这是为了更加清楚变量的意思 其实不用这个@Mgrid变量的

-- 如果@maxlevels输入的是NULL,把他变为最大的INT类型
SET @maxlevels = COALESCE(@maxlevels, 2147483647);

WHILE @lvl <= @maxlevels and @mgrid is not null --注意这里是小于等于 而且新插入的经理不可以为NULL,为null说明是老大了
BEGIN
--插入当前经理
INSERT INTO @Mgrs(empid, lvl) VALUES(@mgrid, @lvl); --这里第一次插入是自己,后面就是各自的经理了
SET @lvl = @lvl + 1;
--获得下一个级别的经理
SET @mgrid = (SELECT mgrid FROM dbo.Employees
WHERE empid = @mgrid);

END

RETURN;
END
GO
SELECT empid, lvl
FROM dbo.fn_managers(8, NULL) AS M;

--查询结果

[c-sharp] view plaincopyprint?

/*
empid lvl
----------- -----------
1 3
2 2
5 1
8 0

*/

SELECT empid, lvl

FROM dbo.fn_managers(8, 2) AS M;


[c-sharp] view plaincopyprint?

/*
empid lvl
----------- -----------
2 2
5 1
8 0
*/

--sql2005



[c-sharp] view plaincopyprint?

DECLARE @empid AS INT, @maxlevels AS INT;
SET @empid = 8;
SET @maxlevels = 2;

WITH MgrsCTE
AS
(
SELECT empid, mgrid, empname, 0 AS lvl
FROM dbo.Employees
WHERE empid = @empid

UNION ALL

SELECT P.empid, P.mgrid, P.empname, C.lvl + 1
FROM MgrsCTE AS C
JOIN dbo.Employees AS P
ON C.mgrid = P.empid
AND C.lvl < @maxlevels--限制递归数...
)
SELECT * FROM MgrsCTE;

C.层次显示

--SQL2000,思路跟下属问题一模一样 只是多了个PATH

[c-sharp] view plaincopyprint?

IF OBJECT_ID('dbo.fn_subordinates3') IS NOT NULL
DROP FUNCTION dbo.fn_subordinates3;
GO
CREATE FUNCTION dbo.fn_subordinates3
(@root AS INT, @maxlevels AS INT = NULL) RETURNS @Subs TABLE
(
empid INT NOT NULL PRIMARY KEY NONCLUSTERED,
lvl INT NOT NULL,
path VARCHAR(900) NOT NULL
UNIQUE CLUSTERED(lvl, empid)
)
AS
BEGIN
DECLARE @lvl AS INT;
SET @lvl = 0;

SET @maxlevels = COALESCE(@maxlevels, 2147483647);

INSERT INTO @Subs(empid, lvl, path)
SELECT empid, @lvl, CAST(empid AS VARCHAR(100))--ZHU YI ZHE LI
FROM dbo.Employees
WHERE empid = @root;

WHILE @@rowcount > 0
AND @lvl < @maxlevels
BEGIN
SET @lvl = @lvl + 1;

INSERT INTO @Subs(empid, lvl, path)
SELECT C.empid, @lvl,
P.path +'-'+ CAST(C.empid AS VARCHAR(100)) --和上面类型保持一致
FROM @Subs AS P
JOIN dbo.Employees AS C
ON P.lvl = @lvl - 1
AND C.mgrid = P.empid;
END

RETURN;
END
GO

--这里的显示分2种

--显示1

select empid ,pos=REPLICATE('-',lvl)+rtrim(empid)

FROM dbo.fn_subordinates3(1, NULL) AS S

ORDER BY PATH

[c-sharp] view plaincopyprint?

/*
empid pos
----------- ------------
1 1
2 -2
4 --4
5 --5
10 ---10
8 ---8
6 --6
3 -3
7 --7
11 ---11
9 ---9
12 ----12
13 ----13
14 ----14
*/

--还有一种

SELECT empid, path

FROM dbo.fn_subordinates3(1, NULL) AS S

ORDER BY PATH

[c-sharp] view plaincopyprint?

/*
empid path
----------- ----------------
1 1
2 1-2
4 1-2-4
5 1-2-5
10 1-2-5-10
8 1-2-5-8
6 1-2-6
3 1-3
7 1-3-7
11 1-3-7-11
9 1-3-7-9
12 1-3-7-9-12
13 1-3-7-9-13
14 1-3-7-9-14
*/

--当然上面的显示方式还很多,自己可以控制 比如不加ORDER 就可以排出不一样的

--SQL2005

[c-sharp] view plaincopyprint?

DECLARE @root AS INT;
SET @root = 1;

WITH SubsCTE
AS
(
SELECT empid, empname, 0 AS lvl,
-- Path of root = '.' + empid + '.'
CAST(CAST(empid AS VARCHAR(10))
AS VARCHAR(MAX)) AS path
FROM dbo.Employees
WHERE empid = @root

UNION ALL

SELECT C.empid, C.empname, P.lvl + 1,

CAST(P.path+'-' + CAST(C.empid AS VARCHAR(10))
AS VARCHAR(MAX)) AS path
FROM SubsCTE AS P
JOIN dbo.Employees AS C
ON C.mgrid = P.empid
)
SELECT empid, REPLICATE(' | ', lvl) + empname AS empname
FROM SubsCTE
ORDER BY path

;

--结果查询

[c-sharp] view plaincopyprint?

/*
empid empname
----------- -------------------------
1 David
2 | Eitan
4 | | Seraph
5 | | Jiru
10 | | | Sean
8 | | | Lilach
6 | | Steve
3 | Ina
7 | | Aaron
11 | | | Gabriel
9 | | | Rita
12 | | | | Emilia
13 | | | | Michael
14 | | | | Didi

*/

D.检测循环中异常

说白了 就是检查表里有没出现1-2-4-1这种头接尾的圈.这在现实中是不可能的.一个经理部可能是它手下的手下.-- ||

我们对表做手脚,把老大的经理改成是它的一个员工

[c-sharp] view plaincopyprint?

UPDATE dbo.Employees SET mgrid = 14 WHERE empid = 1;
DECLARE @root AS INT;
SET @root = 1;

WITH SubsCTE
AS
(
SELECT empid, empname, 0 AS lvl,
CAST( CAST(empid AS VARCHAR(10))
AS VARCHAR(MAX)) AS path,
-- 第一层是不会出现圈圈的
0 AS cycle --0表示没有圈 1表示出现圈
FROM dbo.Employees
WHERE empid = @root

UNION ALL

SELECT C.empid, C.empname, P.lvl + 1,
CAST(P.path + '-'+CAST(C.empid AS VARCHAR(10))
AS VARCHAR(MAX)) AS path,
-- 这里需要检查
CASE WHEN P.path LIKE '%' + CAST(C.empid AS VARCHAR(10)) + '%'
THEN 1 ELSE 0 END
FROM SubsCTE AS P
JOIN dbo.Employees AS C
ON C.mgrid = P.empid
where p.cycle=0 --保证不会继续在错误的地方继续循环下去
)
SELECT empid, empname, cycle, path
FROM SubsCTE
where cycle=1

--查询结果

[c-sharp] view plaincopyprint?

/*
empid empname cycle path
----------- ------------------------- ----------- ------------------
1 David 1 1-3-7-9-14-1

*/

--这样管理员可以轻易找到那个错误的地方.

改过来:UPDATE dbo.Employees SET mgrid = NULL WHERE empid = 1;

2.具体化路径

这里就是新增加2列,一列是级别 一列是节点路径,这样可以避免每次都去计算.

2个优点:不需要递归,只需要基于集合 ;查询可以使用到路径索引

a.维护数据

1.添加不管理员工的员工



[c-sharp] view plaincopyprint?

SET NOCOUNT ON;
USE tempdb;
GO
IF OBJECT_ID('dbo.Employees') IS NOT NULL
DROP TABLE dbo.Employees;
GO
CREATE TABLE dbo.Employees
(
empid INT NOT NULL PRIMARY KEY NONCLUSTERED,
mgrid INT NULL REFERENCES dbo.Employees,
empname VARCHAR(25) NOT NULL,
salary MONEY NOT NULL,
lvl INT NOT NULL,
path VARCHAR(900) NOT NULL UNIQUE CLUSTERED
);
CREATE UNIQUE INDEX idx_unc_mgrid_empid ON dbo.Employees(mgrid, empid);
GO
IF OBJECT_ID('dbo.usp_insertemp') IS NOT NULL
DROP PROC dbo.usp_insertemp;
GO
CREATE PROC dbo.usp_insertemp
@empid INT,
@mgrid INT,
@empname VARCHAR(25),
@salary MONEY
AS

SET NOCOUNT ON;

-- 如果插入的是一个没有经理的老大
IF @mgrid IS NULL
INSERT INTO dbo.Employees(empid, mgrid, empname, salary, lvl, path)
VALUES(@empid, @mgrid, @empname, @salary,
0, CAST(@empid AS VARCHAR(10)));
--插入有经理的小弟
ELSE
INSERT INTO dbo.Employees(empid, mgrid, empname, salary, lvl, path)
SELECT @empid, @mgrid, @empname, @salary,
lvl + 1, path +'-'+ CAST(@empid AS VARCHAR(10))
FROM dbo.Employees
WHERE empid = @mgrid;
GO

EXEC dbo.usp_insertemp
@empid = 1, @mgrid = NULL, @empname = 'David', @salary = $10000.00;
EXEC dbo.usp_insertemp
@empid = 2, @mgrid = 1, @empname = 'Eitan', @salary = $7000.00;
EXEC dbo.usp_insertemp
@empid = 3, @mgrid = 1, @empname = 'Ina', @salary = $7500.00;
EXEC dbo.usp_insertemp
@empid = 4, @mgrid = 2, @empname = 'Seraph', @salary = $5000.00;
EXEC dbo.usp_insertemp
@empid = 5, @mgrid = 2, @empname = 'Jiru', @salary = $5500.00;
EXEC dbo.usp_insertemp
@empid = 6, @mgrid = 2, @empname = 'Steve', @salary = $4500.00;
EXEC dbo.usp_insertemp
@empid = 7, @mgrid = 3, @empname = 'Aaron', @salary = $5000.00;
EXEC dbo.usp_insertemp
@empid = 8, @mgrid = 5, @empname = 'Lilach', @salary = $3500.00;
EXEC dbo.usp_insertemp
@empid = 9, @mgrid = 7, @empname = 'Rita', @salary = $3000.00;
EXEC dbo.usp_insertemp
@empid = 10, @mgrid = 5, @empname = 'Sean', @salary = $3000.00;
EXEC dbo.usp_insertemp
@empid = 11, @mgrid = 7, @empname = 'Gabriel', @salary = $3000.00;
EXEC dbo.usp_insertemp
@empid = 12, @mgrid = 9, @empname = 'Emilia', @salary = $2000.00;
EXEC dbo.usp_insertemp
@empid = 13, @mgrid = 9, @empname = 'Michael', @salary = $2000.00;
EXEC dbo.usp_insertemp
@empid = 14, @mgrid = 9, @empname = 'Didi', @salary = $1500.00

;

----检测

SELECT empid, mgrid, empname, salary, lvl, path

FROM dbo.Employees

ORDER BY path;

[c-sharp] view plaincopyprint?

/*
empid mgrid empname salary lvl path
----------- ----------- ------------------------- --------------------- ----------- ---------
1 NULL David 10000.00 0 1
2 1 Eitan 7000.00 1 1-2
4 2 Seraph 5000.00 2 1-2-4
5 2 Jiru 5500.00 2 1-2-5
10 5 Sean 3000.00 3 1-2-5-10
8 5 Lilach 3500.00 3 1-2-5-8
6 2 Steve 4500.00 2 1-2-6
3 1 Ina 7500.00 1 1-3
7 3 Aaron 5000.00 2 1-3-7
11 7 Gabriel 3000.00 3 1-3-7-11
9 7 Rita 3000.00 3 1-3-7-9
12 9 Emilia 2000.00 4 1-3-7-9-12
13 9 Michael 2000.00 4 1-3-7-9-13
14 9 Didi 1500.00 4 1-3-7-9-14

*/

2.移动子树

比如说某个部门来了个新老大,原来部门老大和手下的人都要跟着他.这个时候表里的路径和级别都要更新

Select Empid, Replicate(' | ', Lvl) + Empname As Empname, Lvl, Path

From Dbo.Employees

Order By Path;


--这个是移动之前的层次分布

[c-sharp] view plaincopyprint?

/*
empid empname lvl path

----------- ----------------- ----------- ------------
1 David 0 1
2 | Eitan 1 1-2
4 | | Seraph 2 1-2-4
5 | | Jiru 2 1-2-5
10 | | | Sean 3 1-2-5-10
6 | | Steve 2 1-2-6
3 | Ina 1 1-3
7 | | Aaron 2 1-3-7
11 | | | Gabriel 3 1-3-7-11
9 | | | Rita 3 1-3-7-9
13 | | | | Michael 4 1-3-7-9-13
14 | | | | Didi 4 1-3-7-9-14
*/

==接下来我们移动

[c-sharp] view plaincopyprint?

IF OBJECT_ID('dbo.usp_movesubtree') IS NOT NULL
DROP PROC dbo.usp_movesubtree;
GO
CREATE PROC dbo.usp_movesubtree
@root INT,--旧经理
@mgrid INT--新经理
AS

SET NOCOUNT ON;

BEGIN TRAN;

UPDATE E
SET lvl = E.lvl + NM.lvl - OM.lvl,-- 级别=当前的级别+(新经理级别-原经理级别)
path = STUFF(E.path, 1, LEN(OM.path), NM.path)
-- 路径=自己的路径移除原来经理那部分再加上新的经理的那部分
--这里的 OM.path 是被替换经理的经理的路径 比如旧经理是1-3-7 那么OM.PATH 就是1-3
--NM.path 是新经理的路径了 比如要换新经理的EMPID是10 所以NM.path 就是 1-2-5-10
--所以如果本来旧经理一个手下比如说是1-3-7-10 现在整个替换过来就是1-2-5-10-7-10
FROM dbo.Employees AS E -- E = 员工
JOIN dbo.Employees AS R -- R = 根
ON R.empid = @root
AND E.path LIKE R.path + '%'
JOIN dbo.Employees AS OM -- OM = 旧经理
ON OM.empid = R.mgrid
JOIN dbo.Employees AS NM -- NM = 新经理
ON NM.empid = @mgrid;

-- 更新旧的经理的经理为新来的经理
UPDATE dbo.Employees SET mgrid = @mgrid WHERE empid = @root;

COMMIT TRAN;
GO

BEGIN TRAN;

EXEC dbo.usp_movesubtree
@root = 7,
@mgrid = 10;

-- After moving subtree
SELECT empid, REPLICATE(' | ', lvl) + empname AS empname, lvl, path
FROM dbo.Employees
ORDER BY path;

ROLLBACK TRAN;

--移动后结果

[c-sharp] view plaincopyprint?

/*
empid empname lvl path

----------- ------------------------------ ----------- ----------------------
1 David 0 1
2 | Eitan 1 1-2
4 | | Seraph 2 1-2-4
5 | | Jiru 2 1-2-5
10 | | | Sean 3 1-2-5-10
7 | | | | Aaron 4 1-2-5-10-7
11 | | | | | Gabriel 5 1-2-5-10-7-11
9 | | | | | Rita 5 1-2-5-10-7-9
12 | | | | | | Emilia 6 1-2-5-10-7-9-12
13 | | | | | | Michael 6 1-2-5-10-7-9-13
14 | | | | | | Didi 6 1-2-5-10-7-9-14
8 | | | Lilach 3 1-2-5-8
6 | | Steve 2 1-2-6
3 | Ina 1 1-3

*/

--这个是移动之后 大家注意观察7 和 10 两位同志

3.移除子树

就是把某个部门从公司取消掉

--首先查看公司部门当前分布



[c-sharp] view plaincopyprint?

SELECT empid, REPLICATE(' | ', lvl) + empname AS empname, lvl, path
FROM dbo.Employees
ORDER BY path;

--查询结果

[c-sharp] view plaincopyprint?

/*
empid empname lvl path

----------- ------------- ------------- -----------
1 David 0 1
2 | Eitan 1 1-2
4 | | Seraph 2 1-2-4
5 | | Jiru 2 1-2-5
8 | | | Lilach 3 1-2-5-8
6 | | Steve 2 1-2-6
3 | Ina 1 1-3
7 | | Aaron 2 1-3-7
11 | | | Gabriel 3 1-3-7-11
9 | | | Rita 3 1-3-7-9
12 | | | | Emilia 4 1-3-7-9-12
13 | | | | Michael 4 1-3-7-9-13
14 | | | | Didi 4 1-3-7-9-14
*/

--接着我们来开始一个部门 比如移除Aaron手下的人

[c-sharp] view plaincopyprint?

BEGIN TRAN;

DELETE FROM dbo.Employees
WHERE path LIKE
(SELECT M.path + '%'
FROM dbo.Employees as M
WHERE M.empid = 7);

--删除之后显示
SELECT empid, REPLICATE(' | ', lvl) + empname AS empname, lvl, path
FROM dbo.Employees
ORDER BY path;

ROLLBACK TRAN;

--查询结果

[c-sharp] view plaincopyprint?

/*
empid empname lvl path

----------- ------------------ ----------- ------------
1 David 0 1
2 | Eitan 1 1-2
4 | | Seraph 2 1-2-4
5 | | Jiru 2 1-2-5
10 | | | Sean 3 1-2-5-10
8 | | | Lilach 3 1-2-5-8
6 | | Steve 2 1-2-6
3 | Ina 1 1-3
*/

4.查询

这里的查询可就轻松多啦 ~因为路径都有啦~

--查询EMPID为3的手下一批人

[c-sharp] view plaincopyprint?

SELECT REPLICATE(' | ', E.lvl - M.lvl) + E.empname
FROM dbo.Employees AS E
JOIN dbo.Employees AS M
ON M.empid = 3 -- root
AND E.path LIKE M.path + '%'
--and E.path LIKE M.path + '_%'--这里那个3号就没了
--and E.lvl - M.lvl <= 2 --限制级数
--WHERE NOT EXISTS --自己本身不是经理的,返回的都是员工
(SELECT *
FROM dbo.Employees AS E2
WHERE E2.mgrid = E.empid);
ORDER BY E.path;

--查询结果

[c-sharp] view plaincopyprint?

/*
------------------
Ina
| Aaron
| | Gabriel
| | Rita
| | | Emilia
| | | Michael
| | | Didi

*/

b.嵌套集合

是书的作者认为用于树建模最完美最高明的解决方案.

惭愧..太难..准备翻第二次书的时候再研究...
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: