Найти родителей и детей с помощью рекурсивного CTE - PullRequest
0 голосов
/ 11 июля 2011

У меня небольшое требование

У меня есть некоторые данные, как в

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 ]

2 голосов
/ 11 июля 2011
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
0 голосов
/ 11 июля 2011
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
0 голосов
/ 11 июля 2011

Вы можете использовать приведенный ниже запрос на выборку для требования

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

Я бы скорее предложил изменить требуемую структуру таблицы, если это возможно, с учетом производительности.

Надеюсь, это полезно.

...