2010-11-17 57 views
7

找到最後比方說,我有如下表:SQL 2005 - 公共表表達式 - 在層次

CREATE TABLE Employees 
(
EmployeeId int PRIMARY KEY NOT NULL, 
ParentEmployeId int REFERENCES Employees(EmployeeId) NULL, 
Name varChar(255) 
) 

所有記錄都有一個主標識和記錄,能夠識別另一個記錄作爲父母。 (我的實際架構不是關於員工,這只是一個簡化版本的插圖,所以如果你有更好的方式處理員工信息,它與這次談話沒有密切關係)。

插入以下記錄:

INSERT INTO Employees VALUES (1, NULL, 'Company President 1') 
INSERT INTO Employees VALUES (2, NULL, 'Company President 2') 

INSERT INTO Employees VALUES (3, 1, 'Company President 1 - VP') 
INSERT INTO Employees VALUES (4, 2, 'Company President 2 - VP') 

INSERT INTO Employees VALUES (5, 3, 'Company President 1 - VP - Secretary') 
INSERT INTO Employees VALUES (6, 4, 'Company President 2 - VP - Secretary') 

INSERT INTO Employees VALUES (7, 5, 'Company President 1 - VP - Secretary - Sandwich Delivery') 

這些插入代表:

Company President 1 
    Company President 1 - VP 
     Company President 1 - VP - Secretary 
      Company President 1 - VP - Secretary - Sandwich Delivery 
Company President 2 
    Company President 2 - VP 
     Company President 2 - VP - Secretary 

我試圖做的是有一個NULL ParentEmployeeId所有的員工,我想找到的最後一個人在鏈,在這個例子中將是「Company President 1 - VP - Secretary - Sandwich Delivery」和「Company President 2 - VP - Secretary」。

我已經得到了以下CTE,它給了我包括嵌套級別在內的所有東西,但我不確定從哪裏去。如果可能,我想避免遊標。

另外,這是非常重要,我在其他地方有邏輯保證員工只能有1個直接下屬。因此,儘管架構在技術上允許,Company President 1將永遠不會列出兩個VP。

WITH EmployeeRec(EmployeeId, ParentEmployeeId, Name, Level) AS 
(
    SELECT 
     EmployeeId, 
     ParentEmployeId, 
     Name, 
     1 as [Level] 
    FROM 
     Employees 
    WHERE 
     ParentEmployeId IS NULL 

    UNION ALL 

    SELECT 
     E.EmployeeId, 
     E.ParentEmployeId, 
     E.Name, 
     R.[Level] + 1 
    FROM 
     Employees E 
    INNER JOIN 
     EmployeeRec R 
    ON 
     E.ParentEmployeId = R.EmployeeId 
) 
SELECT * FROM EmployeeRec 

回答

6

跟蹤您的主僱員ID可以讓您加入最後一級的結果以保留您需要的記錄。

WITH EmployeeRec(Master, EmployeeId, ParentEmployeeId, Name, Level) AS 
(
    SELECT 
     [Master] = EmployeeId, 
     EmployeeId, 
     ParentEmployeId, 
     Name, 
     1 as [Level] 
    FROM 
     Employees 
    WHERE 
     ParentEmployeId IS NULL 

    UNION ALL 

    SELECT 
     R.Master, 
     E.EmployeeId, 
     E.ParentEmployeId, 
     E.Name, 
     R.[Level] + 1 
    FROM 
     Employees E 
    INNER JOIN 
     EmployeeRec R 
    ON 
     E.ParentEmployeId = R.EmployeeId 
) 
SELECT * 
FROM EmployeeRec er 
     INNER JOIN (
      SELECT Master, Level = MAX(Level) 
      FROM EmployeeRec 
      GROUP BY Master 
     ) m ON m.Master = er.Master 
       AND m.Level = er.Level 
+0

固定的語法和1,因爲我覺得'EmployeeRec'比我的中間結果集:) – AakashM 2010-11-17 18:24:56

+0

@AakashM,得益於更優雅你的自我加入,我已經錯過了。在我的辯護中,我寫這篇文章時沒有訪問SQL Server實例。 – 2010-11-17 18:25:36

+0

...並且我回復了贊成:) – 2010-11-17 18:26:11

3

這裏的關鍵是要跟蹤在遞歸CTE頂級父:

;WITH EmployeeRec(
    EmployeeId, ParentEmployeeId, UltimateGrandbossEmployeeId, Name, Level) 
AS 
(
    SELECT 
     EmployeeId, 
     ParentEmployeeId, 
     EmployeeId UltimateGrandbossEmployeeId, 
     Name, 
     1 as [Level] 
    FROM 
     Employees 
    WHERE 
     ParentEmployeeId IS NULL 

    UNION ALL 

    SELECT 
     E.EmployeeId, 
     E.ParentEmployeeId, 
     R.UltimateGrandbossEmployeeId, 
     E.Name, 
     R.[Level] + 1 
    FROM 
     Employees E 
    INNER JOIN 
     EmployeeRec R 
    ON 
     E.ParentEmployeeId = R.EmployeeId 
) 

...形成中間CTE捕捉「自下而上」的水平。 ..

SELECT 
    UltimateGrandbossEmployeeId, 
    Name, 
    ROW_NUMBER() OVER (PARTITION BY UltimateGrandbossEmployeeId 
         ORDER BY Level Desc) BottomUp 
FROM EmployeeRec 
) 

...每個最終grandboss,選擇最深切的孩子:

SELECT 
    UltimateGrandbossEmployeeId, DeepestChildName 
FROM 
    Inter 
WHERE 
    BottomUp = 1 

(串聯所有這些代碼片段在一起,形成有兩個CTE的單個查詢和SELECT

結果:

1 Company President 1 - VP - Secretary - Sandwich Delivery 
2 Company President 2 - VP - Secretary 

您可以JOIN這回Employee得到ultiamte grandboss名稱或曲目CTE中的名稱,在您的實際情況中是有意義的。