在SQL Server中将分层数据转换为Path Style flat(分支)样式

时间:2018-04-25 17:39:30

标签: sql sql-server

我有一个以下分层格式的表   - 输入示例1

Dim_Name    |Dim_Parent
G. Manager  |#root
Assistant   |G. Manager
Manager     |G.Manager
Sr Associate|Manager
Associate   | Sr Associate

我希望将其转换为平面(“分支”视图)表   - 输出示例1

LVL3      |LVL2      |LVL1        |LVL0      
G. Manager|Manager   |Sr Associate|Associate
G. Manager|Assistant |Assistant   |Assistant

我的要求是拥有一个可以处理未知层次结构的解决方案。例如,对于以下输入:   - 输入示例2

Dim_Name    |Dim_Parent
G. Manager  |#root
Assistant   |G. Manager
Manager     |G.Manager

我的输出应该是 - 输出示例2

LVL1      |LVL0         
G. Manager|Manager   
G. Manager|Assistant 

任何建议都会有所帮助。

1 个答案:

答案 0 :(得分:0)

如果要多次运行,请取消注释drop table语句。同时调整输入,无论初始数据集中的内容是什么,它仍然可以正常工作

--drop table ##final
--drop table #temp 
create table #temp

(rowid int identity, Dim_Name varchar(max),  Dim_Parent varchar(max))
insert #temp
values 
('G. Manager',  '#root'),
('Assistant' ,  'G. Manager') ,
('Manager'   ,  'G. Manager'),
('Sr Associate', 'Manager'),
('Mid Associate', 'Manager'),
('Jr Associate', 'Manager'),
('Rookie'   , 'Jr Associate'),
('Newbie'   , 'Jr Associate'),
('Associate'   , 'Sr Associate'),
('Intern'   , 'Associate') 

 --drop table ##holding
 create table ##holding   (rowid int, value1 varchar(max), value2 varchar(max)) 

 declare @inneriterator int = 1
 declare @iterator int = 1
 declare @maxrowid int = (select max(rowid) from #temp)
 declare @dim_parent varchar(max)

 while @iterator<= @maxrowid
 begin

 select @dim_parent=dim_name
 from #temp 
 where rowid=@iterator

 while @inneriterator<= @maxrowid
 begin 

 insert ##holding
 select @iterator, @dim_parent, dim_name 
 from #temp where dim_parent=@dim_parent

 set @inneriterator=@inneriterator+1
 end
 set @inneriterator=1
 set @iterator=@iterator+1

 end 

 select distinct rowid aa, 'left join ##holding b' a,  'on a.value2=b' b,  '.value1'c 
 into #a from ##holding   
 where 
 value2 in (select value1 from ##holding)

 select cast(row_number() over(order by aa) as varchar(max)) rownum,* into #b  from #a

 create table #c (rowid int identity, alias varchar(max), a varchar(max), b varchar(max), c varchar(max))
 insert #c 

 select null,  
 a+cast(rownum as varchar(max))a, null b,c 
 from #b 

 update #c set alias='b'+cast(rowid as varchar(max)) 

 select * , lag(alias, 1,'a') over(order by rowid) previousalias into #d
 from #c

 update #d set b='on '+previousalias+'.value2='+alias  

 --select * from #d

 select @maxrowid=max(rowid) from #d 
 declare @string varchar(max)= ''
 set @iterator=1 
 while @iterator<=@maxrowid
 begin 
 select @string=case when @iterator=@maxrowid 
 then @string+alias+'.value2 as [Level '+ cast(rowid+2 as varchar(max))+'] '
 else @string+alias+'.value2 as [Level '+ cast(rowid+2 as varchar(max))+'], ' end 
 from #d
 where rowid=@iterator
 set @iterator=@iterator+1
 end

 set @string=
 case when exists(select 1 from #d) 
 then 'select distinct a.value1 [Level 1], a.value2 [Level 2], '+@string+ 'into ##final from ##holding a '
 else 'select distinct a.value1 [Level 1], a.value2 [Level 2] '+@string+ 'into ##final from ##holding a '
 end

 set @iterator=1
 while @iterator<=@maxrowid
 begin
 set @string=@string+(select a+' '+b+' '+c+' ' 
 from #d where rowid=@iterator)
 set @iterator=@iterator+1
 end  

 --select @string

 exec(''+@string+' where a.rowid=1')

 create table #columns (rowid int identity, column_name varchar(max))
 insert #columns
 select distinct column_name from tempdb.INFORMATION_SCHEMA.COLUMNS where table_name='##final'

 declare @column_name varchar(max)
 set @iterator=1
 select @maxrowid=max(rowid) from #columns
 while @iterator<=@maxrowid 
 begin
 select @column_name=column_name from #columns where rowid=@iterator

 exec('if not exists (select 1 from ##final where ['+@column_name+']  is not null)
 begin
 alter table ##final
 drop column ['+@column_name+'] end')

 set @iterator=@iterator+1
 end

    set @iterator =1
declare @iterator2 int
declare @fieldname varchar(max)
declare @lastfieldname varchar(max)
while @iterator<=(select max(rowid) from #columns)
begin 

select @fieldname=column_name from #columns
where rowid=@iterator
set @iterator2=@iterator-1
set @lastfieldname='Level '+cast(@iterator2 as varchar(max))

--select @lastfieldname

if  @fieldname in (select column_name from tempdb.INFORMATION_SCHEMA.COLUMNS where table_name='##final')
and @lastfieldname in (select column_name from tempdb.INFORMATION_SCHEMA.COLUMNS where table_name='##final')
exec('update ##final set ['+@fieldname+']= case when ['+@fieldname+'] is null then 
[Level '+@iterator2+']  
else ['+@fieldname+'] end ')


set @iterator=@iterator+1
end 

select * from ##final

--drop table #columns
--drop table #a
--drop table #b 
--drop table #c 
--drop table #d