【问题标题】:sql adjacency list CTE : filter/limit to only complete pathssql邻接列表CTE:过滤/限制为仅完整路径
【发布时间】:2012-05-03 19:34:07
【问题描述】:

我已经构建了一个函数/查询,它返回 [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) 按 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 的 Trees and Hierarchies in SQL for Smarties - 但到目前为止我还没有思考如何正确限制结果......

这里是代码;

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  

【问题讨论】:

    标签: sql tree adjacency-list


    【解决方案1】:

    在 SQLFiddle here 上查看答案。

    这是我的策略:让你的递归 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
    

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

    【讨论】:

    • 谢谢你,Chad - 我将使用它作为我的解决方案:这“将其限制为最后一个节点不用作另一个节点的父节点的行”。非常适合我在主查询中需要做的所有事情!
    【解决方案2】:

    尝试下一个解决方案

    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
    

    【讨论】:

    • 这行得通 - 看起来您将其限制为仅第一次出现“到该点”的路径是的?
    • 我的查询采用一个字符串(路径)并搜索它包含多少其他字符串。如果它返回结果 1 则它是最终路径。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-12-21
    • 1970-01-01
    • 1970-01-01
    • 2015-10-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多