行列轉換

先看個例子

 

--行列轉換函數 PIVOT UNPIVOT
create table pp (VerdorID int,Emp1 int ,Emp2 int,Emp3 int,Emp4 int,Emp5 int)
GO
INSERT INTO pp VALUES (1,4,3,5,4,4)
INSERT INTO pp VALUES (2,4,1,5,5,5)
INSERT INTO pp VALUES (3,4,3,5,4,4)
INSERT INTO pp VALUES (4,4,2,5,5,4)
INSERT INTO pp VALUES (5,5,1,5,5,5)


GO
select VerdorID ,employee,orders into #test
from
(select verdorID,emp1,emp2,emp3,emp4,emp5 from pp)p
unpivot
(orders for employee in
 (emp1,emp2,emp3,emp4,emp5)
 ) as unpvt
GO

select * from pp
select * from #test


select verdorID,emp1,emp2,emp3,emp4 from #test
pivot
(count(orders)for employee in
([emp1],[emp2],[emp3],[emp4])
) as p
---PS :UNPIVOT 並不完全是 PIVOT 的逆操作。PIVOT 會執行一次聚合,從而將多個可能的行合併爲輸出中的單個行。而 UNPIVOT 不會重現原始表值表達式的結果,因爲行已經被合併了。另外,UNPIVOT 的輸入中的 NULL 不會顯示在輸出中,然而在執行 PIVOT 操作之前輸入中可能會含有原始的 NULL 值。

 

關於一般的行列轉換(Sql2000)我們必須用 select ...case...語句
參照http://blog.csdn.net/talantlee/articles/353798.aspx
針對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() 的類型。此列不能爲 p_w_picpath 或 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)
作者:愛新覺羅.毓華(十八年風雨,守得冰山雪蓮花開)
時間:2008-03-09
地點:廣東深圳
說明:普通行列轉換(version 1.0)僅針對sql server 2000提供靜態和動態寫法,version 2.0增加sql server 2005的有關寫法。

問題:假設有張學生成績表(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 0 end) 語文,
max(case 課程 when '數學' then 分數 else 0 end) 數學,
max(case 課程 when '物理' then 分數 else 0 end) 物理
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 (select distinct 課程 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 tb group by 課程
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 0 end) 語文,
max(case 課程 when '數學' then 分數 else 0 end) 數學,
max(case 課程 when '物理' then 分數 else 0 end) 物理,
cast(avg(分數*1.0) as decimal(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 (select distinct 課程 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) as decimal(18,2)) 平均分 , sum(分數) 總分 from tb group by 姓名) n
where m.姓名 = n.姓名

--SQL SERVER 2005 動態SQL。
declare @sql varchar(8000)
select @sql = isnull(@sql + ',' , '') + 課程 from tb group by 課程
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 '語文' then 1 when '數學' then 2 when '物理' then 3 end

--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/3 as decimal(18,2)) from tb
union all
select 姓名 as 姓名 , 課程 = '總分' , 分數 = 語文 + 數學 + 物理 from tb
) t
order by 姓名 , case 課程 when '語文' then 1 when '數學' then 2 when '物理' then 3 when '平均分' then 4 when '總分' then 5 end

drop table tb

 

 

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