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,看你喜歡的電影,勞逸結合,效率更高哦!

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