使用递归CTE查找父和子

时间:2011-07-11 03:37:55

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

我有一个小要求

我有

下的一些数据
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

3 个答案:

答案 0 :(得分: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'

;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

答案 1 :(得分:0)

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

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

为此,您需要具有用户定义的Split函数,该函数返回表变量

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

我建议尽可能改变所需的表格结构,并考虑到效果。

希望这很有用。

答案 2 :(得分:0)

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