在RDBMS中操作 图 树 层次结构 等特殊的数据结构时,我们通常采用2个主要方法:
1.基于迭代/递归
2.具体化描述数据结构的附加信息。
一般模型有:员工组织图(树,层次结构);料表--BOM(有向图);道路系统(无向循环图)
1.迭代/递归
迭代可以迭代图的一个节点,也可以迭代一个层次.后者比前者要快很多.
实现方法:SQL2000通过UDF(用户自定义函数),SQL2005使用CTE。
a.下属问题(通俗说,求子节点)
--这里我使用书上的员工表(表内容如下)
- 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方法:
- 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
- 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;
--查询结果
- /*
- 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
- 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;
--查询结果
- /*
- 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;
- /*
- 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方法类似
- 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;
--还有一种偏方:但是不推荐 虽然结果正确 但是会报错
- 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 控制递归
--查询结果
- /*
- empid empname lvl
- ----------- ------------------------- -----------
- 3 Ina 0
- 7 Aaron 1
- 9 Rita 2
- 11 Gabriel 2
- 消息 530,级别 16,状态 1,第 4 行
- 语句被终止。完成执行语句前已用完最大递归 2。
- */
b.祖先(通俗说:求父节点)
其实思路跟子节点差不多
--SQL2000
- 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;
--查询结果
- /*
- empid lvl
- ----------- -----------
- 1 3
- 2 2
- 5 1
- 8 0
- */
SELECT empid, lvl
FROM dbo.fn_managers(8, 2) AS M;
- /*
- empid lvl
- ----------- -----------
- 2 2
- 5 1
- 8 0
- */
--sql2005
- 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
- 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
- /*
- 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
- /*
- 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
- 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
;
--结果查询
- /*
- 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这种头接尾的圈.这在现实中是不可能的.一个经理部可能是它手下的手下.-- ||
我们对表做手脚,把老大的经理改成是它的一个员工
- 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
--查询结果
- /*
- 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.添加不管理员工的员工
- 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;
- /*
- 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;
--这个是移动之前的层次分布
- /*
- 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
- */
==接下来我们移动
- 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;
--移动后结果
- /*
- 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.移除子树
就是把某个部门从公司取消掉
--首先查看公司部门当前分布
- SELECT empid, REPLICATE(' | ', lvl) + empname AS empname, lvl, path
- FROM dbo.Employees
- ORDER BY path;
--查询结果
- /*
- 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手下的人
- 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;
--查询结果
- /*
- 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的手下一批人
- 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;
--查询结果
- /*
- ------------------
- Ina
- | Aaron
- | | Gabriel
- | | Rita
- | | | Emilia
- | | | Michael
- | | | Didi
- */
b.嵌套集合
是书的作者认为用于树建模最完美最高明的解决方案.
惭愧..太难..准备翻第二次书的时候再研究...