2017-09-22 32 views
0

在SQL Server中,我有這個簡單的表,我試圖讓所有的員工列表與他們的域管理器:最大遞歸100已經聲明完成前被耗盡(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 
+0

的可能的複製[最大遞歸100已經聲明完成前被耗盡(https://stackoverflow.com/questions/9650045/the-maximum-recursion-100-has-been-exhausted-before-語句完成) –

+0

不是重複的,因爲這是邏輯錯誤。但我不明白這個邏輯。爲什麼'mgrid' /'dommgr'在所有結果記錄中都是4,但這些記錄在表中有不同的managerid?你能用文字解釋你想要達到的目標嗎?如果你在遞歸CTE中的根查詢將包含一個類似'WHERE mgrid IS NULL'的過濾器,那麼你將避免無限遞歸,但結果仍然不同。 –

+0

域管理員的ID是'4',但沒有員工有4個作爲'mgrid',這就是爲什麼你沒有得到你期望的結果。沒有人管理員是'域管理器' –

回答

1

你有兩名員工被其在經理ID referenecing對方,所以一個是另一個的經理。這導致了無限遞歸。在遞歸樹中還有一個空白,因爲域管理器沒有被引用到任何地方。您已經通過將Luke的mgrid更改爲4來修正了示例數據。現在不存在任何差距,也沒有任何問題了。 但是你也沒有遞歸的根條目,第一個查詢沒有過濾器。因爲要找到每一個員工的域管理器的IDS

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 /根點是Domain Manager

您可以使用此查詢。這個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 
+0

這太完美了!感謝和抱歉與數據混淆。 – SBF

0

錯誤消息被升高,因爲該數據包含盧克和維拉之間的循環引用。

如果添加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) 水平HIERARCHYID不爲空, 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的水平,我們得到他們之間的距離

+0

這將涉及表結構或視圖的更改,這在我的情況下是不可能的 – SBF

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 
相關問題