解析自引用表时CTE中的无限循环

时间:2012-12-05 07:18:16

标签: sql sql-server sql-server-2008 tsql recursive-query

我正在使用以下公用表表达式来解析自引用表。但CTE不起作用,产生无限循环并产生错误:

  

Msg 530,Level 16,State 1,Line 1声明终止。该   在语句完成之前,最大递归100已经用尽。

如何修改此CTE以使其正常工作?

SET NOCOUNT ON;
USE tempdb;

IF OBJECT_ID('dbo.Employees', 'U') IS NOT NULL DROP TABLE dbo.Employees;

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 > 0)
);

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

; with  Tree as
        (
        SELECT  empid
        ,       mgrid
        ,       1 as lv
        ,       1 as level1
        ,       null as level2
        ,       null as level3
        ,       null as level4
        ,       null as level5
        FROM    Employees
        WHERE   empid = 1 and mgrid = 1
        UNION ALL
        SELECT  E.empid
        ,       E.mgrid
        ,       T.lv + 1
        ,       T.level1
        ,       case when T.lv = 1 then E.empid else t.level2 end
        ,       case when T.lv = 2 then E.empid else t.level3 end
        ,       case when T.lv = 3 then E.empid else t.level4 end
        ,       case when T.lv = 4 then E.empid else t.level5 end
        FROM    Employees AS E
        JOIN    Tree T
        ON      E.mgrid = T.empid
        )
select  *
from Tree
order by empid

首选输出

+-------+-------+----+--------+--------+--------+--------+--------+
| empid | mgrid | lv | level1 | level2 | level3 | level4 | level5 |
+-------+-------+----+--------+--------+--------+--------+--------+
|     1 |     1 |  1 |      1 | NULL   | NULL   | NULL   | NULL   |
|     2 |     1 |  2 |      1 | 2      | NULL   | NULL   | NULL   |
|     3 |     1 |  2 |      1 | 3      | NULL   | NULL   | NULL   |
|     4 |     2 |  3 |      1 | 2      | 4      | NULL   | NULL   |
|     5 |     2 |  3 |      1 | 2      | 5      | NULL   | NULL   |
|     6 |     2 |  3 |      1 | 2      | 6      | NULL   | NULL   |
|     7 |     3 |  3 |      1 | 3      | 7      | NULL   | NULL   |
|     8 |     5 |  4 |      1 | 2      | 5      | 8      | NULL   |
|     9 |     7 |  4 |      1 | 3      | 7      | 9      | NULL   |
|    10 |     5 |  4 |      1 | 2      | 5      | 10     | NULL   |
|    11 |     7 |  4 |      1 | 3      | 7      | 11     | NULL   |
|    12 |     9 |  5 |      1 | 3      | 7      | 9      | 12     |
|    13 |     9 |  5 |      1 | 3      | 7      | 9      | 13     |
|    14 |     9 |  5 |      1 | 3      | 7      | 9      | 14     |
+-------+-------+----+--------+--------+--------+--------+--------+

1 个答案:

答案 0 :(得分:21)

无限循环的原因是empid=mgrid的第一个记录。要处理此问题,您应该包含一个累积字段(在此示例中为levels)以存储您已处理的mgrid,并检查此列表中是否已emid以避免循环。

这是一个查询:

with Tree as
        (
        SELECT  empid
        ,       mgrid
        ,       1 as lv
        ,       1 as level1
        ,       null as level2
        ,       null as level3
        ,       null as level4
        ,       null as level5
        ,       cast(mgrid as varchar(max)) levels  
        FROM    Employees
        WHERE   empid = 1 and mgrid = 1
        UNION ALL
        SELECT  E.empid
        ,       E.mgrid
        ,       T.lv + 1
        ,       T.level1
        ,       case when T.lv = 1 then E.empid else t.level2 end
        ,       case when T.lv = 2 then E.empid else t.level3 end
        ,       case when T.lv = 3 then E.empid else t.level4 end
        ,       case when T.lv = 4 then E.empid else t.level5 end
        ,       T.levels+','+cast(E.mgrid as varchar(max)) levels   

          FROM    Employees AS E
        JOIN    Tree T
        ON      E.mgrid = T.empid 
                and (','+T.levels+',' 
                      not like 
                     '%,'+cast(E.empid as varchar(max))+',%')
        )
select  *
from Tree
order by empid

这是SQLFiddle demo