SQL Server 2005 中行列转换(Pivot 和 UNPivot的使用)

更多好的文章就在 blog.haoitsoft.com,请大家多多支持!


针对sql2005  系统提供两个新的关键字 PIVOT 和UNPIVOT可用来作此类操作.
语法规则
<pivot_clause> ::=
        ( aggregate_function ( value_column )
        FOR pivot_column
        IN ( <column_list> )
    )
<unpivot_clause> ::=
        ( value_column FOR pivot_column IN ( <column_list> ) )
table_source PIVOT <pivot_clause>


指定基于 table_source 对 pivot_column 进行透视。table_source 是表或表表达式。输出是包含 table_source 中 pivot_column 和 value_column 列之外的所有列的表。table_source 中 pivot_column 和 value_column 列之外的列被称为透视运算符的组合列。
PIVOT 对输入表执行组合列的分组操作,并为每个组返回一行。此外,input_table 的 pivot_column 中显示的 column_list 中指定的每个值,输出中都对应一列。


有关详细信息,请参阅“备注”部分和使用 PIVOT 和 UNPIVOT。
aggregate_function
系统或用户定义的聚合函数。聚合函数应该对空值固定不变。对空值固定不变的聚合函数在求聚合值时不考虑组中的空值。
不允许使用 COUNT(*) 系统聚合函数。


value_column
PIVOT 运算符的值列。与 UNPIVOT 一起使用时,value_column 不能是输入 table_source 中的现有列的名称。

FOR pivot_column
PIVOT 运算符的透视列。pivot_column 必须属于可隐式或显式转换为 nvarchar() 的类型。此列不能为 image 或 rowversion。
使用 UNPIVOT 时,pivot_column 是从 table_source 中提取的输出列的名称。table_source 中不能有该名称的现有列。


IN ( column_list )
在 PIVOT 子句中,列出 pivot_column 中将成为输出表的列名的值。该列表不能指定被透视的输入 table_source 中已存在的任何列名。
在 UNPIVOT 子句中,列出 table_source 中将被提取到单个 pivot_column 中的列。

建立测试环境
CREATE TABLE pvt (VendorID int, Emp1 int, Emp2 int,
Emp3 int, Emp4 int, Emp5 int)
GO
INSERT INTO pvt VALUES (1,4,3,5,4,4)
INSERT INTO pvt VALUES (2,4,1,5,5,5)
INSERT INTO pvt VALUES (3,4,3,5,4,4)
INSERT INTO pvt VALUES (4,4,2,5,5,4)
INSERT INTO pvt VALUES (5,5,1,5,5,5)
GO
--行列合并使用 UNPIVOT
SELECT VendorID, Employee, Orders into #test
FROM
   (SELECT VendorID, Emp1, Emp2, Emp3, Emp4, Emp5
   FROM pvt) p
UNPIVOT
   (Orders FOR Employee IN        --//Orders是用来作第1列的,Employee 是合并后的列名
      (Emp1, Emp2, Emp3, Emp4, Emp5)
)AS unpvt
GO
select * from #test
--行列转换 使用PIVOT
select VendorID,Emp1,emp2,emp3,emp4 from #test
pivot
(count(orders) For Employee in   --//可以使用相应的聚合函数
([Emp1],[emp2],[emp3],[emp4])
) as p

请注意,UNPIVOT 并不完全是 PIVOT 的逆操作。PIVOT 会执行一次聚合,从而将多个可能的行合并为输出中的单个行。而 UNPIVOT 不会重现原始表值表达式的结果,因为行已经被合并了。另外,UNPIVOT 的输入中的 NULL 不会显示在输出中,然而在执行 PIVOT 操作之前输入中可能会含有原始的 NULL 值。

 

/*
标题:普通行列转换(version 2.0)


问题:假设有张学生成绩表(tb)如下:
姓名 课程 分数
张三 语文 74
张三 数学 83
张三 物理 93
李四 语文 74
李四 数学 84
李四 物理 94
想变成(得到如下结果):
姓名 语文 数学 物理
---- ---- ---- ----
李四 74   84   94
张三 74   83   93
-------------------
*/

create table tb(姓名 varchar(10) , 课程varchar(10) , 分数int)
insert into tb values('张三' , '语文' ,74)
insert into tb values('张三' , '数学' ,83)
insert into tb values('张三' , '物理' ,93)
insert into tb values('李四' , '语文' ,74)
insert into tb values('李四' , '数学' ,84)
insert into tb values('李四' , '物理' ,94)
go

--SQL SERVER 2000 静态SQL,指课程只有语文、数学、物理这三门课程。(以下同)
select 姓名 as 姓名 ,
 
max(case 课程when'语文'then 分数else 0end) 语文,
 
max(case 课程when'数学'then 分数else 0end) 数学,
 
max(case 课程when'物理'then 分数else 0end) 物理
from tb
group by 姓名

--SQL SERVER 2000 动态SQL,指课程不止语文、数学、物理这三门课程。(以下同)
declare @sql varchar(8000)
set @sql ='select 姓名'
select @sql =@sql+' , max(case 课程 when'''+ 课程+''' then 分数 else 0 end) ['+ 课程 + ']'
from (selectdistinct 课程from tb) as a
set @sql =@sql+' from tb group by 姓名'
exec(@sql)

--SQL SERVER 2005 静态SQL。
select * from (select*from tb) a pivot (max(分数)for 课程in (语文,数学,物理)) b

--SQL SERVER 2005 动态SQL。
declare @sql varchar(8000)
select @sql =isnull(@sql+'],[' ,'')+ 课程from tbgroupby 课程
set @sql ='['+@sql+']'
exec ('select * from (select * from tb) a pivot (max(分数) for 课程 in ('+@sql+')) b')

---------------------------------

/*
问题:在上述结果的基础上加平均分,总分,得到如下结果:
姓名 语文 数学 物理 平均分 总分
---- ---- ---- ---- ------ ----
李四 74   84   94   84.00  252
张三 74   83   93   83.33  250
*/

--SQL SERVER 2000 静态SQL。
select 姓名 姓名,
 
max(case 课程when'语文'then 分数else 0end) 语文,
 
max(case 课程when'数学'then 分数else 0end) 数学,
 
max(case 课程when'物理'then 分数else 0end) 物理,
 
cast(avg(分数*1.0)asdecimal(18,2)) 平均分,
 
sum(分数) 总分
from tb
group by 姓名

--SQL SERVER 2000 动态SQL。
declare @sql varchar(8000)
set @sql ='select 姓名'
select @sql =@sql+' , max(case 课程 when'''+ 课程+''' then 分数 else 0 end) ['+ 课程 + ']'
from (selectdistinct 课程from tb) as a
set @sql =@sql+' , cast(avg(分数*1.0) as decimal(18,2)) 平均分 , sum(分数) 总分 from tb group by 姓名'
exec(@sql)

--SQL SERVER 2005 静态SQL。
select m.* , n.平均分 , n.总分from
(
select * from (select*from tb) a pivot (max(分数)for 课程in (语文,数学,物理)) b) m,
(
select 姓名 ,cast(avg(分数*1.0)asdecimal(18,2)) 平均分 , sum(分数) 总分from tbgroupby 姓名) n
where m.姓名 = n.姓名

--SQL SERVER 2005 动态SQL。
declare @sql varchar(8000)
select @sql =isnull(@sql+',' ,'')+ 课程from tbgroupby 课程
exec ('select m.* , n.平均分 , n.总分 from
(select * from (select * from tb) a pivot (max(分数) for 课程 in (
'+@sql+')) b) m ,
(select 姓名 , cast(avg(分数*1.0) as decimal(18,2)) 平均分 , sum(分数) 总分 from tb group by 姓名) n
where m.姓名 = n.姓名
')

drop table tb   

------------------
--
----------------

/*
问题:如果上述两表互相换一下:即表结构和数据为:
姓名 语文 数学 物理
张三 74  83  93
李四 74  84  94


想变成(得到如下结果):


姓名 课程 分数
---- ---- ----
李四 语文 74
李四 数学 84
李四 物理 94
张三 语文 74
张三 数学 83
张三 物理 93
--------------
*/

create table tb(姓名 varchar(10) , 语文int , 数学int , 物理 int)
insert into tb values('张三',74,83,93)
insert into tb values('李四',74,84,94)
go

--SQL SERVER 2000 静态SQL。
select * from
(
select 姓名 , 课程='语文' , 分数= 语文from tb
union all
select 姓名 , 课程='数学' , 分数= 数学from tb
union all
select 姓名 , 课程='物理' , 分数= 物理from tb
) t
order by 姓名 , case 课程when'语文'then1when'数学'then2when'物理'then3end

--SQL SERVER 2000 动态SQL。
--
调用系统表动态生态。
declare @sql varchar(8000)
select @sql =isnull(@sql+' union all' ,'' )+' select 姓名 , [课程] ='+quotename(Name ,'''')+' , [分数] ='+quotename(Name)+' from tb'
from syscolumns
where name!= N'姓名'and ID = object_id('tb')--表名tb,不包含列名为姓名的其它列
order by colid asc
exec(@sql+' order by 姓名')

--SQL SERVER 2005 动态SQL。
select 姓名 , 课程 , 分数from tb unpivot (分数for 课程in([语文] ,[数学] ,[物理])) t

--SQL SERVER 2005 动态SQL,同SQL SERVER 2000 动态SQL。

--------------------
/*

问题:在上述的结果上加个平均分,总分,得到如下结果:


姓名 课程   分数
---- ------ ------
李四 语文   74.00
李四 数学   84.00
李四 物理   94.00
李四 平均分 84.00
李四 总分   252.00
张三 语文   74.00
张三 数学   83.00
张三 物理   93.00
张三 平均分 83.33
张三 总分   250.00
------------------
*/

select * from
(
select 姓名 as 姓名 , 课程 = '语文' , 分数= 语文 from tb
union all
select 姓名 as 姓名 , 课程 = '数学' , 分数= 数学 from tb
union all
select 姓名 as 姓名 , 课程 = '物理' , 分数= 物理 from tb
union all
select 姓名 as 姓名 , 课程 = '平均分' , 分数=cast((语文+ 数学+ 物理)*1.0/3asdecimal(18,2))from tb
union all
select 姓名 as 姓名 , 课程 = '总分' , 分数= 语文 + 数学 + 物理from tb
) t
order by 姓名 , case 课程when'语文'then1when'数学'then2when'物理'then3when'平均分'then4when'总分'then5end

drop table tb



更多文章,请移步到http://blog.haoitsoft.com,谢谢!
还可以通过好电影网:www.haotv8.cc,看你喜欢的电影,劳逸结合,效率更高哦!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章