1

我已经构建了一个函数/查询,该函数/查询将 [paths] 作为邻接列表返回,如下所示,它运行良好,但是我试图弄清楚如何将结果限制为仅完整的路径,而不是深度(n)的每次迭代。

例如,在下面显示的以下结果中,只有这些是有效的,我想要返回或过滤的完整路径:

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

测试

select * from fn_Get_SubTreePaths('S11', 11) order by DPath

结果

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

换句话说 - 我如何才能确定仅包含遍历/深度中最底部项目的那些路径?

任何帮助将不胜感激!我正在阅读 Joe Celko 为 Smarties 编写的 SQL 中的树和层次结构 - 但到目前为止,我还没有思考如何正确限制结果......

这是代码;

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]

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  
4

2 回答 2

2

请参阅此处在 SQLFiddle 上工作的答案。

这是我的策略:让你的递归 CTE 返回当前节点以及它之前的节点。然后,您可以将其加入自身并将其限制为最后一个节点未用作另一个节点的父节点的行。

这是您的函数的外观(用您的参数替换 'S11' 和 15):

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

如果您想了解它的工作原理,请告诉我,我可以尝试更好地解释它。

于 2012-05-03T20:05:35.107 回答
0

尝试下一个解决方案

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
于 2012-05-03T20:15:38.393 回答