SQL - Transpose a Column to Row

匿名 (未验证) 提交于 2019-12-03 01:09:02

问题:

Ok, I've searched but can't find anything as specific as I'm trying to do here. I have two different tables that I need info from. This is a sample schema - similar to what I'm working from:

create table Nodes(   Caption varchar(max),   IP_Address varchar(max),   NodeID varchar(max) );  insert into Nodes (Caption, IP_Address, NodeID) values ('dev-srvr', '10.0.0.1', '29023'); insert into Nodes (Caption, IP_Address, NodeID) values ('prod-srvr', '10.0.2.1', '29056'); insert into Nodes (Caption, IP_Address, NodeID) values ('test-srvr', '10.1.1.1', '29087');  create table Volumes(   Caption varchar(max),   NodeID varchar(max) );  insert into Volumes (NodeID, Caption) values ('29023', '/'); insert into Volumes (NodeID, Caption) values ('29023', '/boot'); insert into Volumes (NodeID, Caption) values ('29023', '/dev/shm'); insert into Volumes (NodeID, Caption) values ('29023', '/home'); insert into Volumes (NodeID, Caption) values ('29056', '/'); insert into Volumes (NodeID, Caption) values ('29056', '/var'); insert into Volumes (NodeID, Caption) values ('29056', '/opt'); insert into Volumes (NodeID, Caption) values ('29087', '/tmp');

I'm attempting to write a query (with a where clause...will have multiple a filters on final version) that will return Node.Caption, IP_Address, and every Volumes.Caption associated (based on NodeID). The number of entries in Volumes.Caption for each NodeID is dynamic and varies from 1 to about 60 or so. All I know how to write is this:

select Nodes.Caption, Nodes.IP_Address, Volumes.Caption as Volume from Nodes with (nolock) inner join Volumes on Nodes.NodeID=Volumes.NodeID where IP_Address like '10.0%'

Which returns the following:

Caption   | IP_Address | Volume --------------------------------- dev-srvr  | 10.0.0.1   | / dev-srvr  | 10.0.0.1   | /boot dev-srvr  | 10.0.0.1   | /dev/shm dev-srvr  | 10.0.0.1   | /home prod-srvr | 10.0.0.1   | /var prod-srvr | 10.0.0.1   | /opt

But what I need is a single ROW per NodeID showing Node.Caption, IP_Address and all matching Volumes if possible. Like this (the final column names are not important...can be anything):

Caption   | IP_Address | Volume1 | Volume2 | Volume3  | Volume 4 ---------------------------------------------------------------- dev-srvr  | 10.0.0.1   | /       | /boot   | /dev/shm | /home prod-srvr | 10.0.0.1   | /var    | /opt

回答1:

There's 100 pivot examples on SO so I wanted to show a way that you could do this, which isn't as good as pivot, but works for your instance. It may not fit your entire dataset and was based on your sample data only.

Of note, your test data doesn't provide the results you claim they do. Probably just a typo on the insert.

create table #Nodes(   Caption varchar(max),   IP_Address varchar(max),   NodeID varchar(max) );  insert into #Nodes (Caption, IP_Address, NodeID) values  ('dev-srvr', '10.0.0.1', '29023'), ('prod-srvr', '10.0.2.1', '29056'), ('test-srvr', '10.1.1.1', '29087');  create table #Volumes(   Caption varchar(max),   NodeID varchar(max) );  insert into #Volumes (NodeID, Caption) values   ('29023', '/'),  ('29023', '/boot'),  ('29023', '/dev/shm'),  ('29023', '/home'),  ('29056', '/'),  ('29056', '/var'),  ('29056', '/opt'),  ('29087', '/tmp');   select      n.Caption,      n.IP_Address,      v.Caption as Volume from #Nodes n inner join #Volumes v on n.NodeID=v.NodeID where IP_Address like '10.0%'  ;with cte as( select      n.Caption,      n.IP_Address,      v.Caption as Volume,     ROW_NUMBER() over (partition by n.caption, IP_Address order by n.caption) as RN from #Nodes n inner join #Volumes v on n.NodeID=v.NodeID where IP_Address like '10.0%')  select     x.caption,     x.IP_Address,     max(Volume1) as Volume1,     max(Volume2) as Volume2,     max(Volume3) as Volume3,     max(Volume4) as Volume4 from(     select         Caption,         IP_Address,         case when RN = 1 then Volume end as Volume1,         case when RN = 2 then Volume end as Volume2,         case when RN = 3 then Volume end as Volume3,         case when RN = 4 then Volume end as Volume4     from cte) x group by x.Caption, x.IP_Address   drop table #Nodes drop table #Volumes

USING DYNAMIC PIVOT

create table #Nodes(   Caption varchar(max),   IP_Address varchar(max),   NodeID varchar(max) );  insert into #Nodes (Caption, IP_Address, NodeID) values  ('dev-srvr', '10.0.0.1', '29023'), ('prod-srvr', '10.0.2.1', '29056'), ('test-srvr', '10.1.1.1', '29087');  create table #Volumes(   Caption varchar(max),   NodeID varchar(max) );  insert into #Volumes (NodeID, Caption) values   ('29023', '/'),  ('29023', '/boot'),  ('29023', '/dev/shm'),  ('29023', '/home'),  ('29056', '/'),  ('29056', '/var'),  ('29056', '/opt'),  ('29087', '/tmp');    DECLARE @DynamicPivotQuery AS NVARCHAR(MAX) DECLARE @ColumnName AS NVARCHAR(MAX)   select      n.Caption,      n.IP_Address,      v.Caption as Volume,     'Volume' + cast(ROW_NUMBER() over (partition by n.caption, IP_Address order by n.caption) as varchar(16)) as Cname     --ROW_NUMBER() over (partition by n.caption, IP_Address order by n.caption) as RN into #staging from #Nodes n inner join #Volumes v on n.NodeID=v.NodeID where IP_Address like '10.0%'     --Get distinct values of the PIVOT Column  SELECT @ColumnName= ISNULL(@ColumnName + ',','')         + QUOTENAME(Cname) FROM (SELECT DISTINCT Cname FROM #staging) AS Cname  --Prepare the PIVOT query using the dynamic  SET @DynamicPivotQuery =    N'SELECT Caption, IP_Address, ' + @ColumnName + '     FROM #staging     PIVOT(MAX(Volume)            FOR Cname IN (' + @ColumnName + ')) AS PVTTable' --Execute the Dynamic Pivot Query EXEC sp_executesql @DynamicPivotQuery    drop table #Nodes drop table #Volumes drop table #staging


转载请标明出处:SQL - Transpose a Column to Row
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!