我们在工作中经常会遇到字典管理,字典管理的表Dictionary结构如下:
SN(主键) FKSN(外键)ID(编号) Title(标题)
1 0 001 信息中心
2 0 002 业务中心
3 1 001 人员管理
4 1 002 产品管理
5 1 003 帐户管理
6 2 001 销售管理
7 2 002 撤单管理
8 2 003 退货管理
9 3 001 性别字典
10 6 001 结算字典
11 9 001 男
12 9 002 女
13 10 001 现金
14 10 002 欠款
这样就构造成了一棵以FKSN=0为根节点的树。
给定任何一个节点的SN 请查询出所有的子节点。(如果SN=13则应该查出“现金” 如果SN=10则查出“结算字典”“现金”和“欠款” 如SN=1 则查出“信息中心” “人员管理” “产品管理” “帐户管理” “性别字典” “男” “女”)
要求写一个接收SN为参数,返回所有子节点的存储过程。
DECLARE @SN INT
SET @SN = 2
DECLARE @Queue TABLE(AutoSN INT IDENTITY(1,1),SN INT)
DECLARE @Visited TABLE(SN INT)
INSERT INTO @Queue(SN)
SELECT SN FROM dbo.DictionaryBAK
WHERE SN = @SN
DECLARE @CurrentSN INT
WHILE ((SELECT count(*) FROM @Queue) > 0)
BEGIN
SELECT TOP 1 @CurrentSN = SN
FROM @Queue
INSERT INTO @Visited(SN)
VALUES(@CurrentSN)
DELETE FROM @Queue
WHERE SN = @CurrentSN
INSERT INTO @Queue(SN)
SELECT SN FROM dbo.DictionaryBAK
WHERE FKSN = @CurrentSN
END
SELECT * FROM dbo.DictionaryBAK
WHERE SN IN (SELECT DISTINCT SN FROM @Visited) 结合上面的存储过程来看下面的问题:
我们在工作中经常会遇到字典管理,字典管理(Dictionary)的表结构如下:
SN(主键) FKSN(外键)ID(编号) Title(标题)CompanySN(公司)
1 0 001 信息中心 44
2 0 002 业务中心 44
3 1 001 人员管理 44
4 1 002 产品管理 44
5 1 003 帐户管理 44
6 2 001 销售管理 44
7 2 002 撤单管理 44
8 2 003 退货管理 44
9 3 001 性别字典 44
10 6 001 结算字典 44
11 9 001 男 44
12 9 002 女 44
13 10 001 现金 44
14 10 002 欠款 44
这样就构造成了一棵以FKSN=0为根节点的树。
我们经常会复制节点,比如在开发环境中复制注册表,在创建公司的时候把我们公司的所有的字典项复制到要创建公司,其实就是把选定的所有子节点重新生成SN和FKSN然后插入到表中。现要求写一存储过程,接收参数(1).复制节点的SN (2).要存放的目标节点的DestSN (3).目标公司关键字CompanySN, 将要复制节点的所有子节点复制到目标公司的目标节点下。
表我已经在数据库里面创建完了,不用临时表,直接在数据库里面书写,第二题是第一题的延伸,谢谢大家了
SN(主键) FKSN(外键)ID(编号) Title(标题)
1 0 001 信息中心
2 0 002 业务中心
3 1 001 人员管理
4 1 002 产品管理
5 1 003 帐户管理
6 2 001 销售管理
7 2 002 撤单管理
8 2 003 退货管理
9 3 001 性别字典
10 6 001 结算字典
11 9 001 男
12 9 002 女
13 10 001 现金
14 10 002 欠款
这样就构造成了一棵以FKSN=0为根节点的树。
给定任何一个节点的SN 请查询出所有的子节点。(如果SN=13则应该查出“现金” 如果SN=10则查出“结算字典”“现金”和“欠款” 如SN=1 则查出“信息中心” “人员管理” “产品管理” “帐户管理” “性别字典” “男” “女”)
要求写一个接收SN为参数,返回所有子节点的存储过程。
DECLARE @SN INT
SET @SN = 2
DECLARE @Queue TABLE(AutoSN INT IDENTITY(1,1),SN INT)
DECLARE @Visited TABLE(SN INT)
INSERT INTO @Queue(SN)
SELECT SN FROM dbo.DictionaryBAK
WHERE SN = @SN
DECLARE @CurrentSN INT
WHILE ((SELECT count(*) FROM @Queue) > 0)
BEGIN
SELECT TOP 1 @CurrentSN = SN
FROM @Queue
INSERT INTO @Visited(SN)
VALUES(@CurrentSN)
DELETE FROM @Queue
WHERE SN = @CurrentSN
INSERT INTO @Queue(SN)
SELECT SN FROM dbo.DictionaryBAK
WHERE FKSN = @CurrentSN
END
SELECT * FROM dbo.DictionaryBAK
WHERE SN IN (SELECT DISTINCT SN FROM @Visited) 结合上面的存储过程来看下面的问题:
我们在工作中经常会遇到字典管理,字典管理(Dictionary)的表结构如下:
SN(主键) FKSN(外键)ID(编号) Title(标题)CompanySN(公司)
1 0 001 信息中心 44
2 0 002 业务中心 44
3 1 001 人员管理 44
4 1 002 产品管理 44
5 1 003 帐户管理 44
6 2 001 销售管理 44
7 2 002 撤单管理 44
8 2 003 退货管理 44
9 3 001 性别字典 44
10 6 001 结算字典 44
11 9 001 男 44
12 9 002 女 44
13 10 001 现金 44
14 10 002 欠款 44
这样就构造成了一棵以FKSN=0为根节点的树。
我们经常会复制节点,比如在开发环境中复制注册表,在创建公司的时候把我们公司的所有的字典项复制到要创建公司,其实就是把选定的所有子节点重新生成SN和FKSN然后插入到表中。现要求写一存储过程,接收参数(1).复制节点的SN (2).要存放的目标节点的DestSN (3).目标公司关键字CompanySN, 将要复制节点的所有子节点复制到目标公司的目标节点下。
表我已经在数据库里面创建完了,不用临时表,直接在数据库里面书写,第二题是第一题的延伸,谢谢大家了
解决方案 »
- 关于递增字段的问题?
- 查看表中最后一条数据
- 级联删除的问题
- 急求一sql语句。
- 高手帮忙给个建议,数据表主键到底使用 IDENTITY(int, 1,1) 还是 nvarchar 等其它类型的问题
- 求一搜索的全过程,我的数据库搜索后符合条件的记录有10万条,现在什么都没用,速度非常慢,请指点一下这样的解决方法?
- 更改对象名的任一部分都可能破坏脚本和存储过程。
- UPDATE动态SQL时,总出现这样的错误,到底哪里错了 高手帮忙分析一下,谢谢了~
- 请问在SQL SERVER 2000里面,怎样让数据库支持越南文?谢谢!
- 求将触发器批量应用的代码写法
- 呼叫dawugui
- 请问这个SQL语句改成一条语句怎么改呀??
楼主又来一个贴..晕
贴个资料你自己参考下吧
/*
RDBMS中操作S图树层次结构等特殊的数据结构时,我们通常采用个主要方法:
一个是基于迭代/递归另外一个是具体化描述数据结构的附加信息。
一般模型有:员工组织图(树,层次结构);料表--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
--这里的显示分种
--显示
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.检测循环中异常
说白了就是检查表里有没出现-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个优点:不需要递归,只需要基于集合;查询可以使用到路径索引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
ASSET 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;
GOEXEC 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--新经理
ASSET 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 是被替换经理的经理的路径比如旧经理是-3-7 那么OM.PATH 就是-3
--NM.path 是新经理的路径了比如要换新经理的EMPID是所以NM.path 就是1-2-5-10
--所以如果本来旧经理一个手下比如说是-3-7-10 现在整个替换过来就是-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
*/
--这个是移动之后大家注意观察和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为的手下一批人
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 + '_%'--这里那个号就没了
--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
*/