使用t-SQL的数据透视表树

时间:2015-01-30 10:28:51

标签: sql sql-server

输入: 我们在表格中有层次结构数据

所需的输出:

对于每个叶子节点,我们希望她的所有父母都在同一行 每个父母在不同的栏目中;一个用于节点代码,一个用于节点名称

我知道我将使用CTE和Dynamic PIVOT,但我仍在搜索

感谢您的帮助,

use [master]
go

if object_id('tempdb..#hierarchy') is not null drop table #hierarchy

create table #hierarchy (
    hierarchy_NodeId nvarchar(256) null
    ,hierarchy_NodeName nvarchar(256) null
    ,hierarchy_ParentId nvarchar(256) null
    ,hierarchy_ParentName nvarchar(1024) null
    ,hierarchy_IsLeaf nvarchar(256) null
    ,hierarchy_level int null
    )
go

insert #hierarchy (hierarchy_NodeId, hierarchy_NodeName, hierarchy_ParentId, hierarchy_ParentName, hierarchy_IsLeaf, hierarchy_level) 
values 
 ('10', 'Hardware', NULL, NULL, 'false', 0)
,('1003', 'Printers & Scanners', '10', 'Hardware', 'false', 1)
,('1005', 'Networking', '10', 'Hardware', 'false', 1)
,('1006', 'Components & Accessories', '10', 'Hardware', 'false', 1)
,('100602', 'Cables', '1006', 'Components & Accessories', 'false', 2)
,('100606', 'Controller Cards', '1006', 'Components & Accessories', 'false', 2)
,('10060601', 'Network Adapters', '100606', 'Controller Cards', 'true', 3)
,('10060602', 'USB Controllers', '100606', 'Controller Cards', 'true', 3)
,('10060209', 'Phone/Modem Cables', '100602', 'Cables', 'true', 3)
,('100502', 'Hubs & Switches', '1005', 'Networking', 'false', 2)
,('100503', 'Network Adapters', '1005', 'Networking', 'false', 2)
,('100507', 'Wireless', '1005', 'Networking', 'false', 2)
,('100509', 'Network Cabling', '1005', 'Networking', 'false', 2)
,('10050901', 'Twisted Pair Cables', '100509', 'Network Cabling', 'false', 3)
,('10050902', 'Coaxial Cables', '100509', 'Network Cabling', 'true', 3)
,('10050903', 'Fiber Cables', '100509', 'Network Cabling', 'false', 3)
,('10050904', 'Patch Cables', '100509', 'Network Cabling', 'true', 3)
,('10050908', 'Special Network Cables', '100509', 'Network Cabling', 'true', 3)
,('10050909', 'Network Cabling Accessories', '100509', 'Network Cabling', 'true', 3)
,('1005090301', 'Multimode Fiber Cables', '10050903', 'Fiber Cables', 'true', 4)
,('1005090302', 'Singlemode Fiber Cables', '10050903', 'Fiber Cables', 'true', 4)
,('1005090102', 'Cat 5e Cables', '10050901', 'Twisted Pair Cables', 'true', 4)
,('1005090103', 'Cat 6 Cables', '10050901', 'Twisted Pair Cables', 'true', 4)
,('1005090104', 'Cat 6a Cables', '10050901', 'Twisted Pair Cables', 'true', 4)
,('10050701', 'Wireless NICs', '100507', 'Wireless', 'true', 3)
,('10050301', 'PCI Network Adapters', '100503', 'Network Adapters', 'true', 3)
,('10050302', 'PCI-E Network Adapters', '100503', 'Network Adapters', 'true', 3)
,('10050305', 'USB Network Adapters', '100503', 'Network Adapters', 'true', 3)
,('10050306', 'Wired Network Adapters', '100503', 'Network Adapters', 'true', 3)
,('10050308', '10/100 Network Adapters', '100503', 'Network Adapters', 'true', 3)
,('10050309', 'Gigabit Network Adapters', '100503', 'Network Adapters', 'true', 3)
,('10050208', 'Switch Expansion Modules', '100502', 'Hubs & Switches', 'true', 3)
,('100305', 'Print Servers', '1003', 'Printers & Scanners', 'false', 2)
,('10030501', 'Wireless Print Servers', '100305', 'Print Servers', 'true', 3)
,('10030502', 'Ethernet Print Servers', '100305', 'Print Servers', 'true', 3)
;

2 个答案:

答案 0 :(得分:0)

我认为这可以为您提供所需的答案。您当然可以动态构建CTE以满足未知深度。

with Parents (IDa, Pa, IDb, Pb, IDc, Pc, IDd, Pd, hierarchy_NodeId, hierarchy_NodeName, hierarchy_ParentId) as
(
    select Cast('' as varchar(50)) as IDa, Cast('' as varchar(50)) as Pa, 
    Cast('' as varchar(50)) as IDb, Cast('' as varchar(50)) as Pb, 
    Cast('' as varchar(50)) as IDc, Cast('' as varchar(50)) as Pc, 
    Cast('' as varchar(50)) as IDd, Cast('' as varchar(50)) as Pd, hierarchy_NodeId, hierarchy_NodeName, hierarchy_ParentId
    from #hierarchy
    where hierarchy_IsLeaf = 'true'
    union All
    select 
    Cast(Case when H.hierarchy_level = 0 then H.hierarchy_NodeId else e.IDa end as varchar(50)) as IDa,
    Cast(Case when H.hierarchy_level = 0 then H.hierarchy_NodeName else e.Pa end as varchar(50)) as Pa,
    Cast(Case when H.hierarchy_level = 1 then H.hierarchy_NodeId else e.IDb end as varchar(50)) as IDb,
    Cast(Case when H.hierarchy_level = 1 then H.hierarchy_NodeName else e.Pb end as varchar(50)) as Pb,
    Cast(Case when H.hierarchy_level = 2 then H.hierarchy_NodeId else e.IDc end as varchar(50)) as IDc,
    Cast(Case when H.hierarchy_level = 2 then H.hierarchy_NodeName else e.Pc end as varchar(50)) as Pc,
    Cast(Case when H.hierarchy_level = 3 then H.hierarchy_NodeId else e.IDc end as varchar(50)) as IDc,
    Cast(Case when H.hierarchy_level = 3 then H.hierarchy_NodeName else e.Pd end as varchar(50)) as Pd, 
    e.hierarchy_NodeId, e.hierarchy_NodeName, H.hierarchy_ParentId
    from #hierarchy H
    inner join Parents e On e.hierarchy_ParentId = H.hierarchy_NodeId
)

select *
from Parents
where Pa = 'Hardware'
;

以下动态版

declare @sql as varchar(4000)
Declare @list as Varchar(100)
Declare @clausesA as Varchar(2000)
Declare @clausesB as Varchar(2000)

Set @list = ''
set @clausesA = ''
set @clausesB = ''

select Cast(hierarchy_level as varchar(50)) as Hlevel into #Levels
from #hierarchy 
Where hierarchy_level < (select Max(hierarchy_level) from #hierarchy)
group by hierarchy_level
order by hierarchy_level

select @list = @list + Case when @list = '' then '' else ', ' end + 'ID' + Hlevel + ', P' + Hlevel
from #Levels
order by HLevel

select @clausesA = @clausesA + Case when @clausesA = '' then 'Select ' else ', ' end 
+ 'Cast('''' as varchar(50)) as ID' + Hlevel + ' , Cast('''' as varchar(50)) as P' + Hlevel
from #Levels
order by HLevel

select @clausesB = @clausesB + Case when @clausesB = '' then 'Select ' else ', ' end 
+ 'Cast(Case when H.hierarchy_level = ' + HLevel + ' then H.hierarchy_NodeId else e.ID' + Hlevel
+ ' end as varchar(50)) as ID' + Hlevel
+ ', Cast(Case when H.hierarchy_level = ' + HLevel + ' then H.hierarchy_NodeName else e.P' 
+ HLevel + ' end as varchar(50)) as P' + HLevel
from #Levels
order by HLevel

select @sql = 
'with Parents (' + @list + ', hierarchy_NodeId, hierarchy_NodeName, hierarchy_ParentId) as
( '
    + @clausesA + ', hierarchy_NodeId, hierarchy_NodeName, hierarchy_ParentId
    from #hierarchy
    where hierarchy_IsLeaf = ''true''
    union All '
    + @clausesB + 
    ', e.hierarchy_NodeId, e.hierarchy_NodeName, H.hierarchy_ParentId
    from #hierarchy H
    inner join Parents e On e.hierarchy_ParentId = H.hierarchy_NodeId
)

select ' + @list + ', hierarchy_NodeId, hierarchy_NodeName
from Parents
where hierarchy_ParentId is null
;'

exec(@sql);

drop table #Levels

答案 1 :(得分:0)

这已经解决,但没有名字只有nideid和parentids,因为在pivot中没有机会将文本放在值中:

declare @str1 nvarchar(max),@str2 nvarchar(max),@tblcount numeric,@str3 nvarchar(max),@level int,@pid nvarchar(max),@nodeid nvarchar(max),@maxnode nvarchar(max),@counter int,@c int,@breaker int;
with cte as (select row_number() over (order by hierarchy_NodeId) as n1,hierarchy_NodeId from hierarchy) select @tblcount = hierarchy_NodeId from cte where n1 in (select max(n1) from cte) order by n1 desc
set @breaker = (select count(*) from hierarchy)

set @c = 0;
set @counter = 0;
set @level = 0;
set @maxnode = @tblcount
set @str3 = ''

while (@tblcount is not null )
begin
set @level =  0
set @counter = @counter +1
set @str3 = @str3 + 'union select '''+convert(varchar(10),@counter) + ''' as seq,'+convert(varchar(10),@tblcount)+'as nodeid,hierarchy_NodeId,hierarchy_Nodename,''col'+convert(varchar(10),@level)+''' as levelid from hierarchy where hierarchy_NodeId = ' + CONVERT(nvarchar(max),@tblcount)
set @pid = (select hierarchy_ParentId  from hierarchy where hierarchy_NodeId = @tblcount)
while (@pid is not null)
begin
set @nodeid = (select hierarchy_NodeId from hierarchy where hierarchy_NodeId = @pid);
set @pid = (select hierarchy_parentid from hierarchy where hierarchy_NodeId = @nodeid);
if (@nodeid is not null)
begin
set @level = @level + 1
set @counter = @counter +1
set @str3 = @str3 + ' union select  '''+convert(varchar(10),@counter) + ''' as seq,'+convert(varchar(10),@tblcount)+'as nodeid,hierarchy_NodeId,hierarchy_Nodename,''col'+convert(varchar(10),@level)+''' as levelid from hierarchy where hierarchy_NodeId = ' + CONVERT(varchar(10),@nodeid)
end
else
break;  
end;
set @c = @c+1;
if (@c = @breaker)
break;
else
with cte as (select row_number() over (order by hierarchy_NodeId) as n1,hierarchy_NodeId from hierarchy) select @tblcount = isnull(hierarchy_NodeId,null) from cte where n1 in (select max(n1) - @c from cte) order by n1 desc

end
set @str3 = SUBSTRING(@str3,6,len(@str3))
if object_id('tempdb..#data') is not null drop table #data
create table #data (seq int,nodeid nvarchar(max) ,hierarchy_NodeId nvarchar(max),hierarchy_Nodename nvarchar(max),col nvarchar(max))
insert into #data exec(@str3)

set @str1 = (select stuff((select distinct ',['+col+']' from #data for xml path('')),1,1,''))
set @str1 = substring(@str1,8,len(@str1))
print @str1
set @str2 = '
select * from(select nodeid,col,hierarchy_NodeId from #data)as s
pivot
(
max(s.hierarchy_NodeId) for s.col in('+@str1+')
)as p
order by nodeid'
execute sp_executesql @str2