SQL Server中的行列轉換問題

 

SQL Server中的行列轉換問題
 
普通行列轉換僅針對sql server 2000提供靜態和動態寫法。
 
增加sql server 2005的有關寫法。
 

PIVOT用於將列值旋轉爲列名(即行轉列),在SQL Server 2000可以用聚合函數配合CASE語句實現

PIVOT的一般語法是:PIVOT(聚合函數(列) FOR 列 in (…) )AS P

完整語法:

table_source

PIVOT(

聚合函數(value_column)

FOR pivot_column

IN(<column_list>)

)

 

UNPIVOT用於將列明轉爲列值(即列轉行),在SQL Server 2000可以用UNION來實現

完整語法:

table_source

UNPIVOT(

value_column

FOR pivot_column

IN(<column_list>)

)

 

注意:PIVOT、UNPIVOT是SQL Server 2005 的語法,使用需修改數據庫兼容級別
 在數據庫屬性->選項->兼容級別改爲   90

 
 
 
經典案例:
 
問題:假設有張學生成績表(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 end) 語文,
  max(case 課程 when '數學' then 分數 else end) 數學,
  max(case 課程 when '物理' then 分數 else end) 物理
from tb
group by 姓名
 
--SQL SERVER 2000 動態SQL,指課程不止語文、數學、物理這三門課程。(以下同)
--變量按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 --同from tb group by課程,默認按課程名排序
set @sql = @sql + ' from tb group by 姓名'
exec(@sql) 
 
 

--使用isnull(),變量先確定動態部分

declare@sqlvarchar(8000)

select@sql=isnull(@sql+',','')+' max(case課程when '''+課程+''' then分數else 0 end)

['+課程+']'

from(selectdistinct課程fromtb)asa      

set@sql='select姓名,'+@sql+' from tb group by姓名'

exec(@sql)

 
 
--SQL SERVER 2005 靜態SQL。
select from tb pivot (max(分數) for 課程 in (語文,數學,物理)) a
 
--SQL SERVER 2005 動態SQL。
--使用isnull()
declare @sql varchar(8000)
select @sql = isnull(@sql + ',' '') + 課程 from tb group by 課程
exec ('select * from tb pivot (max(分數) for 課程 in (' + @sql + ')) a')
 
 

--使用stuff()

declare@sqlvarchar(8000)

set@sql=''  --初始化變量@sql

select@sql=@sql+','+課程fromtbgroupby課程--變量多值賦值

set@sql=stuff(@sql,1,1,'')--去掉首個','

set@sql='select * from tb pivot (max(分數) for課程in ('+@sql+'))a'

exec(@sql)

 
-----------------------------------------------------------------------
 
/*
問題:在上述結果的基礎上加平均分,總分,得到如下結果:
姓名 語文 數學 物理 平均分 總分 
---- ---- ---- ---- ------ ----
李四 74   84   94   84.00  252
張三 74   83   93   83.33  250
*/
 
--SQL SERVER 2000 靜態SQL。
select 姓名 姓名,
  max(case 課程 when '語文' then 分數 else end) 語文,
  max(case 課程 when '數學' then 分數 else end) 數學,
  max(case 課程 when '物理' then 分數 else 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。
--使用isnull()
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    
 
 

 

--使用stuff()

--

declare@sqlvarchar(8000)

set@sql=''  --初始化變量@sql

select@sql=@sql+','+課程fromtbgroupby課程--變量多值賦值

--select @sql = @sql + ','+課程from (select distinct課程from tb)a

set@sql=stuff(@sql,1,1,'')--去掉首個','

set@sql='select m.* , n.總分,n.平均分from

(select * from (select * from tb) a pivot (max(分數) for課程in ('+@sql+')) b) m ,

(select姓名,sum(分數)總分, cast(avg(分數*1.0) as decimal(18,2))平均分from tb group

by姓名) n

where m.姓名= n.姓名'

exec(@sql)

 
------------------------------------------------------------------
------------------------------------------------------------------
 
/*
問題:如果上述兩表互相換一下:即表結構和數據爲:
姓名 語文 數學 物理
張三 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 when '數學' then 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 d
ecimal(18,2)) from tb
 union all
 select 姓名 as 姓名 , 課程 = '總分' , 分數 = 語文 + 數學 + 物理 from tb) t
order by 姓名 , case 課程 when '語文' then when '數學' then when '物理' then 3
 when '平均分' then when '總分' then end
 
drop table tb
 
 
 
 

 

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