SQL - 将列转置为行

时间:2017-02-02 18:34:17

标签: sql sql-server-2008 tsql 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');
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');

我尝试编写一个查询(带有where子句...将在最终版本上有多个过滤器),它将返回Node.Caption,IP_Address和每个与相关的Volumes.Caption(基于NodeID) 。 Volumes.Caption中每个NodeID的条目数是动态的,从1到大约60左右不等。我所知道的就是这个:

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%'

返回以下内容:

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

但我需要的是每个NodeID的单个ROW,如果可能的话,显示Node.Caption,IP_Address和所有匹配的卷。像这样(最后的列名不重要......可以是任何东西):

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 个答案:

答案 0 :(得分:1)

在SO上有100个透视示例,所以我想展示一种方法,你可以做到这一点,它不像枢轴一样好,但适合你的实例。它可能不适合您的整个数据集,仅基于您的样本数据。

值得注意的是,您的测试数据并未提供您声称的结果。可能只是插页上的拼写错误。

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

使用动态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