在RDBMS中操作S图 树 层次结构等特殊的数据结构时,我们通常采用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;

解决方案 »

  1.   

    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
    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.   


    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 是被替换经理的经理的路径 比如旧经理是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       
     
                                                                 
    */  本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/feixianxxx/archive/2009/11/01/4753783.aspx
    --这个是移动之后 大家注意观察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.嵌套集合
    是书的作者认为用于树建模最完美最高明的解决方案.
    惭愧..太难..准备翻第二次书的时候再研究... ----第一阶段学习完成
      

  3.   

    这个必须要顶 BOM一直是难点
      

  4.   

    学习呀,BOM这个东西一直就没好好看过怕怕