【问题标题】:Find Parent and Child using Recursive CTE使用递归 CTE 查找父子节点
【发布时间】:2011-10-02 11:56:25
【问题描述】:

我有一个小要求

我有一些数据如下

Data
-----
A,B,C
I,J,K
A,D

DDL 如下

Declare @t (Data varchar(50))
Insert into @t Select 'A,B,C' Union all Select 'I,J,K' union all Select 'A,D'
Select * from @t

我要做的是(输出)

Parent  Child
------- -------
Null    A
Null    I
A       B
A       C
I       J
J       K
A       D

到目前为止,我的方法如下但不起作用

;With cte as ( 
  SSELECT Parent = null, Child = substring(data, 1, CHARINDEX(',',data)-1) 
    FROM @t
  Union all
  SELECT t.child, substring(t.data, CHARINDEX(',',t.data)+1, LEN(t.data)) 
    FROM @t t
    JOIN cte c ON c.child <> t.child )
Select * from cte

【问题讨论】:

    标签: sql sql-server-2005 tsql common-table-expression


    【解决方案1】:
    declare @T table (Data varchar(50))
    
    insert into @T
    select 'A,B,C' union all 
    select 'I,J,K' union all
    select 'A,D'
    
    ;with Split as
    (
      select row_number() over(order by (select 1)) as RowID,
             1 as Lvl,
             cast(left(Data, charindex(',', Data+',')-1) as varchar(50)) as Value,
             stuff(Data+',', 1, charindex(',', Data+','), '') as Data
      from @T
      where len(Data) > 0
      union all
      select RowID,
             lvl + 1 as Lvl,
             cast(left(Data, charindex(',', Data)-1) as varchar(50)) as Value,
             stuff(Data, 1, charindex(',', Data), '') as Data
      from Split
      where len(Data) > 0
    )
    select distinct
           P.Value as Parent,
           C.Value as Child
    from Split as C
      left outer join Split as P
        on C.Lvl = P.Lvl + 1 and
           C.RowID = P.RowID
    

    结果:

    Parent   Child
    ------   -----
    NULL     A
    NULL     I
    A        B
    A        D
    B        C
    I        J
    J        K
    

    【讨论】:

    • 只想检查,这是考虑性能的最佳方式。我已经检查过,似乎交叉应用和拆分功能似乎是性能更好的选择。
    • @suryakiran - 我没有对此进行任何性能测试。我不认为像这样拆分字符串是应该经常做的事情。也许只有当您需要通过规范化数据库结构来修复不良的数据库设计时。
    • 我建议在数据库设计中随着表的增长,这不会给出乐观的输出。
    • 我认为我的版本在这里表现最好
    【解决方案2】:

    您可以使用以下选择查询来满足需求

    DECLARE @t TABLE (Data VARCHAR(50))
    INSERT INTO @t SELECT 'A,B,C' UNION ALL SELECT 'I,J,K' UNION ALL SELECT 'A,D'
    
    SELECT DISTINCT b.Item AS Child,
    CASE WHEN 
    CHARINDEX(b.Item,a.Data)=1 
    THEN null 
    ELSE SUBSTRING(a.data, 1,CHARINDEX(',',a.data)-1) END AS Parent  FROM @t a CROSS APPLY dbo.split(a.Data,',') b
    

    为此,您需要有一个用户定义的拆分函数,该函数返回表变量

    CREATE FUNCTION [dbo].[Split]  
    (  
     @ItemList VARCHAR(4000),  
     @delimiter VARCHAR(10)  
    )  
    RETURNS @IDTable TABLE (ID INT IDENTITY(1,1),Item VARCHAR(500))  
    AS  
    BEGIN  
    -- This function is used to split up multi-value parameters  
     DECLARE @tempItemList NVARCHAR(4000)  
     SET @tempItemList = @ItemList  
    
     DECLARE @i INT  
     DECLARE @Item NVARCHAR(4000)  
    
     SET @tempItemList = REPLACE (@tempItemList, ' ' + @delimiter, @delimiter)  
     SET @tempItemList = REPLACE (@tempItemList, @delimiter + ' ', @delimiter)  
     SET @i = CHARINDEX(@delimiter, @tempItemList)   
    
     WHILE (LEN(@tempItemList) > 0)  
     BEGIN  
      IF @i = 0  
       SET @Item = @tempItemList  
      ELSE  
       SET @Item = LEFT(@tempItemList, @i -1)  
    
      INSERT INTO @IDTable(Item) VALUES(@Item)  
    
      IF @i = 0  
       SET @tempItemList = ''  
      ELSE  
       SET @tempItemList = RIGHT(@tempItemList, LEN(@tempItemList) - (@i + LEN(@delimiter)-1))  
    
      SET @i = CHARINDEX(@delimiter, @tempItemList)  
     END  
     RETURN  
    END
    

    我宁愿建议尽可能更改所需的表结构,同时考虑性能。

    希望这是有用的。

    【讨论】:

      【解决方案3】:
      DECLARE @t TABLE (Data VARCHAR(50)) 
      INSERT INTO @t SELECT 'A,B,C' UNION ALL SELECT 'I,J,K' UNION ALL SELECT 'A,D' 
      
      ;WITH cte as (  
      SELECT 
      CAST(null AS VARCHAR(50)) Parent, 
      Child = Left(data, CHARINDEX(',',data)-1), 
      CAST(Stuff(data, 1, CHARINDEX(',',data), '') + ',' as VARCHAR(50)) Leftover 
      FROM @t 
      UNION ALL 
      SELECT cte.Child, 
      Left(cte.leftover, CHARINDEX(',',cte.leftover)-1), 
      CAST(Stuff(cte.leftover, 1, CHARINDEX(',',cte.leftover ), '') as VARCHAR(50))
      FROM cte
      WHERE cte.leftover <> '')
      SELECT DISTINCT parent, child FROM cte 
      

      结果:

      parent   child
      -------- -----
      NULL    A
      NULL    I
      A       B
      A       D
      B       C
      I       J
      J       K
      

      【讨论】:

        猜你喜欢
        • 2011-04-25
        • 1970-01-01
        • 2014-05-19
        • 1970-01-01
        • 1970-01-01
        • 2017-05-04
        • 1970-01-01
        • 2020-10-05
        相关资源
        最近更新 更多