2012-04-06 9 views
5

Así que tiene dos tablas estructuradas de esta manera:nodo raíz get T-SQL en la jerarquía

CREATE TABLE #nodes(node int NOT NULL); 
ALTER TABLE #nodes ADD CONSTRAINT PK_nodes PRIMARY KEY CLUSTERED (node); 

CREATE TABLE #arcs(child_node int NOT NULL, parent_node int NOT NULL); 
ALTER TABLE #arcs ADD CONSTRAINT PK_arcs PRIMARY KEY CLUSTERED (child_node, parent_node); 

INSERT INTO #nodes(node) 
VALUES (1), (2), (3), (4), (5), (6), (7); 

INSERT INTO #arcs(child_node, parent_node) 
VALUES (2, 3), (3, 4), (2, 6), (6, 7); 

Si tengo dos nodos, digamos 1 y 2. Quiero una lista de sus nodos raíz. En este caso, sería 1, 4 y 7. ¿Cómo puedo escribir una consulta para obtener esa información?

Intenté escribirlo pero me encontré con el problema de que no puedo usar una combinación IZQUIERDA en la parte recursiva de un CTE por algún motivo desconocido. Aquí está la consulta que funcionaría si se me permitiera hacer una UNIÓN IZQUIERDA.

WITH root_nodes 
AS (
    -- Grab all the leaf nodes I care about and their parent 
    SELECT n.node as child_node, a.parent_node 
    FROM #nodes n 
    LEFT JOIN #arcs a 
     ON n.node = a.child_node 
    WHERE n.node IN (1, 2) 

    UNION ALL 

    -- Grab all the parent nodes 
    SELECT rn.parent_node as child_node, a.parent_node 
    FROM root_nodes rn 
    LEFT JOIN #arcs a -- <-- LEFT JOINS are Illegal for some reason :(
     ON rn.parent_node = a.child_node 
    WHERE rn.parent_node IS NOT NULL 
) 
SELECT DISTINCT rn.child_node as root_node 
FROM root_nodes rn 
WHERE rn.parent_node IS NULL 

¿Hay alguna forma de que pueda reestructurar la consulta para obtener lo que quiero? No puedo reestructurar los datos y realmente preferiría alejarme de las mesas temporales o tener que hacer algo caro.

Gracias, Raúl

Respuesta

5

¿Qué hay de mover el LEFT JOIN fuera del CTE?

WITH root_nodes 
AS (
    -- Grab all the leaf nodes I care about 
    SELECT NULL as child_node, n.node as parent_node 
    FROM #nodes n 
    WHERE n.node IN (1, 2) 

    UNION ALL 

    -- Grab all the parent nodes 
    SELECT rn.parent_node as child_node, a.parent_node 
    FROM root_nodes rn 
     JOIN #arcs a 
     ON rn.parent_node = a.child_node 
) 
SELECT DISTINCT rn.parent_node AS root_node 
FROM root_nodes rn 
    LEFT JOIN #arcs a 
    ON rn.parent_node = a.child_node 
WHERE a.parent_node IS NULL 

El conjunto de resultados es de 1, 4, 7.

+0

Eso es brillante! No sé por qué no pensé en eso. Tengo una pregunta, cuando miro el plan de ejecución está haciendo que la izquierda se una a un escaneo de índice agrupado. ¿Por qué no elegiría una búsqueda como lo está haciendo la parte recursiva? – HaxElit

+0

No sé por qué. Traté de agregar 'CON (ÍNDICE (PK_arcs))', pero resulta que, en este pequeño conjunto de datos, una búsqueda es en realidad un poco más caro que un escaneo. Tal vez con un gran conjunto de datos, el optimizador elegirá una búsqueda en su lugar. –

+0

Los escaneos no son necesariamente algo malo. Con este pequeño conjunto de datos, es más eficiente absorber todo el índice agrupado de una vez en lugar de hacer las búsquedas. Estoy seguro de que hay un punto de inflexión. –

Cuestiones relacionadas