2010-11-17 11 views
7

Diciamo che ho la seguente tabella:SQL 2005 - Tavola espressione comune - Trova scorso a gerarchia

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

Tutti i record hanno un identificatore primario e le registrazioni sono in grado di identificare un altro record come un genitore. (Il mio schema reale non riguarda dipendenti, questa è solo una versione semplificata per illustrazione quindi se hai un modo migliore di gestire le informazioni dei dipendenti non è pertinente a questa conversazione.)

I seguenti record sono inseriti :

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') 

Questi inserti rappresentano:

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 

quello che sto cercando di fare è per tutti i dipendenti che hanno un valore NULL ParentEmployeeId voglio trovare l'ultima persona al catena, che in questo esempio sarebbe "Company President 1 - VP - Secretary - Sandwich Delivery" e "Company President 2 - VP - Secretary".

Ho il seguente CTE che mi dà tutto incluso il livello di nidificazione ma non sono sicuro di dove andare da qui. Mi piacerebbe evitare i cursori, se possibile.

Inoltre, e questo è molto importante, ho la logica altrove che garantisce che un dipendente può avere solo 1 subordinato diretto. Quindi, sebbene lo schema lo consenta tecnicamente, Company President 1 non avrà mai due VP elencati.

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 

risposta

6

Tenere traccia del master EmployeeID consente di unire i risultati con l'ultimo livello per conservare i record necessari.

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

fissa la sintassi e +1 perché penso che la vostra auto-join di 'EmployeeRec' è più elegante rispetto al mio gruppo di risultati intermedi :) – AakashM

+0

@AakashM, grazie, ho perso. A mia difesa, sto scrivendo questo senza accesso a un'istanza di SQL Server. –

+0

... e ho restituito il favore :) –

3

La chiave qui è quello di tenere traccia del genitore di livello superiore nel CTE ricorsiva:

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

... formano un CTE intermedio per catturare il 'bottom-up' di livello. ..

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

... per ogni grandboss finale, selezionare il proprio figlio più profonda:

SELECT 
    UltimateGrandbossEmployeeId, DeepestChildName 
FROM 
    Inter 
WHERE 
    BottomUp = 1 

(concatenare insieme tutti questi frammenti di codice in modo da formare una singola query con due CTE e un SELECT)

Risultati:

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

È possibile JOIN questo torna a Employee per ottenere ultiamte grandboss nomi o pista i nomi nel CTE, ha senso nella tua situazione attuale.