lista de adyacencia sql CTE: filtrar/limitar solo rutas completas
Frecuentes
Visto 550 equipos
1
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
dE TRATAMIENTOS
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
2 Respuestas
2
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
0
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 sql tree adjacency-list or haz tu propia pregunta.
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