lista de adyacencia sql CTE: filtrar/limitar solo rutas completas

Creé una función/consulta que devuelve [rutas] como una lista de adyacencia como se muestra a continuación y funciona bien, sin embargo, estoy tratando de descubrir cómo limitar los resultados solo a rutas completas, no a cada iteración de profundidad (n).

por ejemplo, de los siguientes resultados que se muestran a continuación, solo estos son caminos COMPLETOS válidos que quiero devolver o filtrar:

S11>S11-LG>V613>V613_Close>B31A>B30>B30A>V30A1>V30A1_Close>B-GARNER>VB1>VB1_Close
S11>S11-LG>V613>V613_Close>B31A>B30>B30A>V30A1>V30A1_Close>B-GARNER>VB1>VB1_Open
S11>S11-LG>V613>V613_Close>B31A>B30>B30A>V30A1>V30A1_Close>B-GARNER>VB1>VB2
S11>S11-LG>V613>V613_Close>B31A>B30>B30A>V30A1>V30A1_Open>V30A2>V30A2_Close>S2-UG
S11>S11-LG>V613>V613_Close>B31A>B30>B30A>V30A1>V30A1_Open>V30A2>V30A2_Open>S1-UG

TEST

seleccionar * de fn_Get_SubTreePaths('S11', 11) ordenar por DPath

resultados

S11>S11-LG
S11>S11-LG>V613
S11>S11-LG>V613>V613_Close
S11>S11-LG>V613>V613_Close>B31A
S11>S11-LG>V613>V613_Close>B31A>B30
S11>S11-LG>V613>V613_Close>B31A>B30>B30A
S11>S11-LG>V613>V613_Close>B31A>B30>B30A>V30A1
S11>S11-LG>V613>V613_Close>B31A>B30>B30A>V30A1>V30A1_Close
S11>S11-LG>V613>V613_Close>B31A>B30>B30A>V30A1>V30A1_Close>B-GARNER
S11>S11-LG>V613>V613_Close>B31A>B30>B30A>V30A1>V30A1_Close>B-GARNER>VB1
S11>S11-LG>V613>V613_Close>B31A>B30>B30A>V30A1>V30A1_Close>B-GARNER>VB1>VB1_Close
S11>S11-LG>V613>V613_Close>B31A>B30>B30A>V30A1>V30A1_Close>B-GARNER>VB1>VB1_Open
S11>S11-LG>V613>V613_Close>B31A>B30>B30A>V30A1>V30A1_Close>B-GARNER>VB1>VB2
S11>S11-LG>V613>V613_Close>B31A>B30>B30A>V30A1>V30A1_Open
S11>S11-LG>V613>V613_Close>B31A>B30>B30A>V30A1>V30A1_Open>V30A2
S11>S11-LG>V613>V613_Close>B31A>B30>B30A>V30A1>V30A1_Open>V30A2>V30A2_Close
S11>S11-LG>V613>V613_Close>B31A>B30>B30A>V30A1>V30A1_Open>V30A2>V30A2_Close>S2-UG
S11>S11-LG>V613>V613_Close>B31A>B30>B30A>V30A1>V30A1_Open>V30A2>V30A2_Open
S11>S11-LG>V613>V613_Close>B31A>B30>B30A>V30A1>V30A1_Open>V30A2>V30A2_Open>S1-UG

En otras palabras, ¿cómo puedo determinar solo aquellas rutas que incluyen el elemento más inferior en el recorrido/profundidad?

¡Cualquier ayuda sería muy apreciada! Estoy leyendo Trees and Hierarchies in SQL for Smarties de Joe Celko, pero hasta ahora no me he dado cuenta de cómo limitar los resultados correctamente...

Aquí está el código;

create function [dbo].[fn_Get_SubTreePaths]( @Start varchar(20), @MaxLevels int)
returns table 
as
RETURN(
    WITH CTE AS (
    SELECT 
        c.DeviceID AS Start,
        CAST(d.DeviceName AS VARCHAR(2000)) AS Path, 
        c.ConnectedDeviceID, 
        1 AS Level
    FROM Connections c
    INNER JOIN Devices d ON d.ID=c.DeviceID
                        AND d.DeviceName=@Start
    UNION ALL
    SELECT 
        r.Start, 
        CAST(r.Path + '>'+ d.DeviceName AS VARCHAR(2000)), 
        --CAST(r.Path + ' -> ' + d.DeviceName + '-' + cast(r.SLevel as varchar) AS VARCHAR),
        c.ConnectedDeviceID, 
        Level = r.Level + 1
    FROM Connections c
    INNER JOIN Devices d ON d.ID=c.DeviceID
                        and d.DeviceName<>@Start
    INNER JOIN CTE r ON c.DeviceID=r.ConnectedDeviceID
      AND r.Level < @MaxLevels
    )

    SELECT 
      DISTINCT a.DPath
      FROM (
        SELECT c.Path + '>' + ISNULL(d.DeviceName,'?')  AS DPath
        FROM CTE c
        INNER JOIN Devices d ON d.ID=c.ConnectedDeviceID
        ) a    
    )                                  




CREATE TABLE [dbo].[Connections](
    [ID] [int] NOT NULL,
    [DeviceID] [int] NULL,
    [ConnectedDeviceID] [int] NULL
 CONSTRAINT [PK_Connections] PRIMARY KEY CLUSTERED 
(
    [ID] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]



CREATE TABLE [dbo].[Devices](
    [ID] [int] IDENTITY(1,1) NOT NULL,
    [DeviceName] [varchar](50) NULL
 CONSTRAINT [PK_Devices] PRIMARY KEY CLUSTERED 
(
    [ID] ASC
) WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

Y la vista utilizada por el CTE:

ALTER view [dbo].[vw_DeviceConnections] as
select 
  c.ID as ID,
  c.DeviceID as DeviceID,
  d.DeviceName,
  d.DeviceType,
  c.ConnectedDeviceID as ConnDeviceID,
  cd.DeviceName as ConnDeviceName,
  cd.DeviceType as ConnDeviceType
from Devices d  
inner join Connections c on
   d.id=c.DeviceID
inner join Devices cd on
   cd.id=c.ConnectedDeviceID  

preguntado el 03 de mayo de 12 a las 20:05

2 Respuestas

Ver la respuesta trabajando en SQLFiddle aquí.

Aquí está mi estrategia: haga que su CTE recursivo devuelva el nodo actual y el nodo anterior. Luego, puede unirlo en sí mismo y restringirlo a filas cuyo último nodo no se usa como padre de otro nodo.

Así es como se vería su función (reemplace 'S11' y 15 con sus parámetros):

WITH CTE AS (
  SELECT
    d.ID AS Start,
    CAST(d.DeviceName AS VARCHAR(2000)) AS Path, 
    d.ID AS node,
    NULL AS parent,
    1 AS Level
  FROM Devices d
  WHERE d.DeviceName= 'S11'
UNION ALL
  SELECT 
    r.Start, 
    CAST(r.Path + '>'+ d.DeviceName AS VARCHAR(2000)), 
    d.ID as node,
    r.node as parent,
    r.Level + 1 as Level
  FROM CTE r
    INNER JOIN Connections c ON c.DeviceID = r.node
    INNER JOIN Devices d ON d.ID = c.ConnectedDeviceID
  WHERE r.Level < 15
),
Trimmed as (
  SELECT L.*
  FROM CTE L
    LEFT JOIN CTE R on L.node = R.parent
  WHERE R.parent IS NULL
)   
SELECT * FROM Trimmed

Avíseme si desea una aclaración sobre cómo funciona, puedo tratar de explicarlo mejor.

contestado el 03 de mayo de 12 a las 22:05

Gracias Chad. Usaré esto como mi solución: esto "restringirlo a filas cuyo último nodo no se usa como padre de otro nodo". es perfecto para lo que necesito hacer todo dentro de la consulta principal. - baley

prueba la siguiente solución

select 
    *
from 
    fn_Get_SubTreePaths('S11', 11) f1
where
    (
        select
            count(*)
        from
            fn_Get_SubTreePaths('S11', 11) f2
        where
            charindex(f1.dPath, f2.dPath) > 0
    ) = 1

order by DPath

contestado el 03 de mayo de 12 a las 21:05

esto funciona: parece que lo está limitando solo a la primera aparición de la ruta "hasta ese punto", ¿sí? - baley

Mi consulta toma una cadena (ruta) y busca cuántas otras cadenas contiene. Si devuelve el resultado 1, entonces es la ruta final. - vadim zin4uk

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.