2010-11-17 10 views
7

Digamos que tengo la siguiente tabla:SQL 2005 - expresión de tabla común - Encuentra pasado en la jerarquía

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

Todos los registros tienen un identificador primario y registros son capaces de identificar un nuevo récord como padre. (Mi esquema actual no es sobre empleados, esta es solo una versión simplificada para ilustración así que si tiene una mejor manera de manejar la información del empleado no es pertinente para esta conversación)

Se insertan los siguientes registros :

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

Estos insertos representan:

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 

lo que estoy tratando de hacer es para todos los empleados que tienen un valor NULL ParentEmployeeId Quiero encontrar la última persona en el cadena, que en este ejemplo sería "Company President 1 - VP - Secretary - Sandwich Delivery" y "Company President 2 - VP - Secretary".

Tengo el siguiente CTE que me da todo, incluido el nivel de anidación, pero no estoy seguro de a dónde ir desde aquí. Me gustaría evitar los cursores si es posible.

Además, y esto es muy importante, tengo la lógica en otro lugar que garantiza que un empleado solo puede tener 1 subordinado directo. Entonces, aunque el esquema técnicamente lo permite, Company President 1 nunca tendrá dos VP en la lista.

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 

Respuesta

6

Hacer un seguimiento de su Master ID de empleado le permite unir los resultados con el último nivel para conservar los registros que necesita.

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

fija de su sintaxis y 1 porque creo que su auto-join de 'EmployeeRec' es más elegante que mi conjunto de resultados intermedios :) – AakashM

+0

@AakashM, gracias, me he perdido eso. En mi defensa, estoy escribiendo esto sin acceso a una instancia de SQL Server. –

+0

... y le devolví el favor :) –

3

La clave aquí es no perder de vista el padre de nivel superior en el CTE recursiva:

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

... forman un CTE intermedia para capturar el 'abajo hacia arriba' nivel. ..

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

... para cada grandboss último, seleccione su hijo más profundo:

SELECT 
    UltimateGrandbossEmployeeId, DeepestChildName 
FROM 
    Inter 
WHERE 
    BottomUp = 1 

(concatenar juntos todos estos fragmentos de código para formar una consulta individual con dos CTE y una SELECT)

Resultados:

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

Puede JOIN esto de nuevo a Employee para obtener ultiamte grandboss nombres o pista los nombres en el CTE, como tiene sentido en su situación real.

Cuestiones relacionadas