语句完成之前已经耗尽最大递归100(SQL Server)

时间:2017-09-22 08:06:30

标签: sql-server common-table-expression

在SQL Server中,我有这个简化的表格,我试图通过他们的域管理器获取所有员工的列表:

IF OBJECT_ID('tempdb.dbo.#employees') IS NOT NULL DROP TABLE #employees

CREATE TABLE #employees (
    empid int,
    empname varchar(50),
    mgrid int,
    func varchar(50)
)

INSERT INTO #employees VALUES(1, 'Jeff', 2, 'Designer')
INSERT INTO #employees VALUES(2, 'Luke', 4, 'Head of designers')
INSERT INTO #employees VALUES(3, 'Vera', 2, 'Designer')
INSERT INTO #employees VALUES(4, 'Peter', 5, 'Domain Manager')
INSERT INTO #employees VALUES(5, 'Olivia', NULL, 'CEO')
;

WITH Emp_CTE AS (
SELECT empid, empname, func, mgrid AS dommgr
    FROM #employees

UNION ALL

SELECT e.empid, e.empname, e.func, e.mgrid AS dommgr
    FROM #employees e
    INNER JOIN Emp_CTE ecte ON ecte.empid = e.mgrid
    WHERE ecte.func <> 'Domain Manager'
)

SELECT * FROM Emp_CTE

所以我想要的输出是:

empid   empname func                dommgr
1       Jeff    Designer            4
2       Luke    Head of designers   4
3       Vera    Designer            4

相反,我得到了这个错误:
Msg 530, Level 16, State 1, Line 17 The statement terminated. The maximum recursion 100 has been exhausted before statement completion.

我做错了什么?实际上是否可以使用CTE

编辑:数据确实存在错误,错误已经消失,但结果不是我想要的:

empid   empname func                dommgr
1       Jeff    Designer            2
2       Luke    Head of designers   4
3       Vera    Designer            2
4       Peter   Domain Manager      5
5       Olivia  CEO                 NULL
4       Peter   Domain Manager      5
1       Jeff    Designer            2
3       Vera    Designer            2

3 个答案:

答案 0 :(得分:1)

你有两名员工在经理人中互相提交,所以一位是另一位经理。这导致了无限递归。递归树中也存在间隙,因为域管理器未在任何地方引用。您已经通过将Luke的mgrid更改为4来修复了样本数据。现在没有差距,也没有任何问题。 但是你也没有递归的根条目,第一个查询没有过滤器。

您可以使用此查询:

WITH DomainManager AS (
SELECT empid, empname, func, dommgr = empid, Hyrarchy = 1
    FROM #employees
    WHERE func = 'Domain Manager'

UNION ALL

SELECT e.empid, e.empname, e.func, dommgr, Hyrarchy = Hyrarchy +1
    FROM #employees e
    INNER JOIN DomainManager dm ON dm.empid = e.mgrid
)

SELECT * FROM DomainManager
WHERE func <> 'Domain Manager'
ORDER BY empid

请注意,CTE的enry / root点是Domain Manager,因为您要查找每个员工域管理员的ID。这个id被传递到层次结构中。最终选择需要过滤掉Domain Manager,因为您只想为每个员工提供他的ID,您不希望将他包含在结果集中。

查询结果为:

empid   empname func                dommgr   Hyrarchy
1       Jeff    Designer               4       3
2       Luke    Head of designers      4       2
3       Vera    Designer               4       3

答案 1 :(得分:0)

引发错误消息,因为数据包含Luke和Vera之间的循环引用。

如果添加hierarchyid字段,则执行分层查询会更容易。 SQL Server提供functions,它返回后代,祖先和层次结构中的级别。 hierarchyid字段可以indexed,从而提高性能。

在员工示例中,您可以添加level字段:

declare @employees table (
    empid int PRIMARY KEY,
    empname varchar(50),
    mgrid int,
    func varchar(50),
    level hierarchyid not null,
    INDEX IX_Level (level)
)

INSERT INTO @employees VALUES
(1, 'Jeff', 2, 'Designer'         ,'/5/4/2/1/'),
(2, 'Luke', 4, 'Head of designers','/5/4/2/'),
(3, 'Vera', 2, 'Designer'         ,'/5/4/2/3/'),
(4, 'Peter', 5, 'Domain Manager'  ,'/5/4/'),
(5, 'Olivia', NULL, 'CEO'         ,'/5/')
;

`声明@employees表(         empid int PRIMARY KEY,         empname varchar(50),         mgrid int,         func varchar(50),         level hierarchyid not null,         INDEX IX_Level(级别)     )

INSERT INTO @employees VALUES
(1, 'Jeff', 2, 'Designer'         ,'/5/4/2/1/'),
(2, 'Luke', 4, 'Head of designers','/5/4/2/'),
(3, 'Vera', 2, 'Designer'         ,'/5/4/2/3/'),
(4, 'Peter', 5, 'Domain Manager'  ,'/5/4/'),
(5, 'Olivia', NULL, 'CEO'         ,'/5/')
;

/5/4/2/1/是hieararchyID值的字符串表示形式。它本质上是层次结构中通向特定行的路径。

要查找域管理员的所有下属,不包括经理本身,您可以写:

with DMs as 
(
    select EmpID,level 
    from @employees
    where func='Domain Manager'
)
select 
    PCs.empid,
    PCs.empname as Name,
    PCs.func as Class,
    DMs.empid as DM,
    PCs.level.GetLevel() as THAC0,
    PCs.level.GetLevel()- DMs.level.GetLevel() as NextLevel
from 
    @employees PCs 
    inner join DMs on PCs.level.IsDescendantOf(DMs.level)=1
where DMs.EmpID<>PCs.empid;

CTE仅用于方便

结果是:

empid   Name    Class               DM  THAC0   NextLevel
1       Jeff    Designer            4   4       2
2       Luke    Head of designers   4   3       1
3       Vera    Designer            4   4       2

CTE返回所有DM及其hierarchyid值。 IsDescendantOf()查询检查行是否是DM的下降。 GetLevel()返回层次结构中行的级别。通过从员工中减去DM的水平,我们得到他们之间的距离

答案 2 :(得分:0)

像其他人说的那样,你在这里有数据问题(维拉)。

IF OBJECT_ID('tempdb.dbo.#employees') IS NOT NULL 
    DROP TABLE #employees

CREATE TABLE #employees (
    empid int,
    empname varchar(50),
    mgrid int,
    func varchar(50)
)

INSERT INTO #employees VALUES(1, 'Jeff', 2, 'Designer')
INSERT INTO #employees VALUES(2, 'Luke', 3, 'Head of designers')
INSERT INTO #employees VALUES(3, 'Vera', 4, 'Designer')         --**mgrid = 4 instead 2**
INSERT INTO #employees VALUES(4, 'Peter', 5, 'Domain Manager')
INSERT INTO #employees VALUES(5, 'Olivia', NULL, 'CEO')

;WITH Emp_CTE AS 
(
    SELECT empid, empname, func, mgrid AS dommgr, 0 AS Done
    FROM #employees
    UNION ALL
    SELECT ecte.empid, ecte.empname, ecte.func, 
        CASE WHEN e.func = 'Domain Manager' THEN e.empid ELSE e.mgrid END AS dommgr, 
        CASE WHEN e.func = 'Domain Manager' THEN 1 ELSE 0 END AS Done
    FROM Emp_CTE AS ecte
        INNER JOIN #employees AS e ON 
            ecte.dommgr = e.empid
    WHERE ecte.Done = 0--emp.func <> 'Domain Manager'
)
SELECT * 
FROM Emp_CTE
WHERE Done = 1