SQL行列轉換方法(詳細例子)

普通行列轉換(version 1.0)僅針對sql server 2000提供靜態和動態寫法,version 2.0增長sql server 2005的有關寫法。sql

問題:假設有張學生成績表(tb)以下:數據庫

姓名 課程 分數測試

張三 語文 74fetch

張三 數學 83ui

張三 物理 93server

李四 語文 74ci

李四 數學 84數學

李四 物理 94string

想變成(獲得以下結果):io

姓名 語文 數學 物理

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

李四 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

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

如下是 dawugui 回貼

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

create table A(id char(3), num1 int, num2 int, num3 int, num4 int)

insert A select '001', 80, 90, 50, 60

insert A select '002', 84, 70, 60, 82

go

--SQL2005實現方法:

select * from A

unpivot

(num for col in([num1],[num2],[num3],[num4]))T2)tmp

--SQL2000實現:

---調用系統表動態生態

declare @s nvarchar(4000)

select @s=isnull(@s+' union all ','')+' select ID,[num]='+quotename(Name,'''')+',Qty='+quotename(Name)+' from A'

from syscolumns

where Name!=N'ID' and ID=object_id('A')--表名A,不包含列名爲ID的其它列

order by colid asc

exec(@s+' order by ID asc,[num] asc')

--生成的靜態語句

select ID,[num]='num1',Qty=[num1] from A union all

select ID,[num]='num2',Qty=[num2] from A union all

select ID,[num]='num3',Qty=[num3] from A union all

select ID,[num]='num4',Qty=[num4] from A

order by ID asc,[num] asc

/*

ID num Qty

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

001 num1 80

001 num2 90

001 num3 50

001 num4 60

002 num1 84

002 num2 70

002 num3 60

002 num4 82

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

*/

--動態方法:

declare @s nvarchar(4000)

select @s=isnull(@s+' union all ','')+' select ID,[num]='+quotename(Name)+' from A'

from syscolumns

where Name!=N'ID' and ID=object_id('A')

order by colid asc

exec(@s+' order by ID asc')

--生成的語句以下:

select ID,[num]=[num1] from A union all

select ID,[num]=[num2] from A union all

select ID,[num]=[num3] from A union all

select ID,[num]=[num4] from A

order by ID asc,[num] asc

/*

ID num

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

001 80

001 90

001 50

001 60

002 82

002 60

002 70

002 84

*/

---drop table A

/*

將表數據旋轉90度(2007-11-19於海南三亞)

將下表數據:

A                    b           c           d           e          

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

x                    1           2           3           4

y                    5           6           7           8

z                    9           10          11          12

轉化成以下結果:

a                    x          y          z         

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

b                    1          5          9

c                    2          6          10

d                    3          7          11

e                    4          8          12

*/

--生成測試數據

create table test1(A varchar(20),b int,c int,d int,e int)

insert into test1 select 'x',1,2 ,3 ,4

insert into test1 select 'y',5,6 ,7 ,8

insert into test1 select 'z',9,10,11,12

go

--生成中間數據表

declare @s varchar(8000)

set @s = 'create table test2(a varchar(20)'

select @s = @s + ',' + A + ' varchar(10)' from test1

set @s = @s + ')'

exec(@s)

print @s

--藉助中間表實現行列轉換

declare @name varchar(20)

declare t_cursor cursor for

select name from syscolumns

where id=object_id('test1') and colid > 1 order by colid

open t_cursor

fetch next from t_cursor into @name

while @@fetch_status = 0

begin

    exec('select ' + @name + ' as t into test3 from test1')

    set @s='insert into test2 select ''' + @name + ''''

    select @s = @s + ',''' + rtrim(t) + '''' from test3

    exec(@s)

    exec('drop table test3')

    fetch next from t_cursor into @name

end

close t_cursor

deallocate t_cursor

--查看行列互換處理結果

select * from test1

select * from test2

--刪除表

drop table test1

drop table test2

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

/*固定的寫法:*/

select t1.* , t2.y , t3.z from

(select a = 'b' , x = b from test1 where a = 'x') t1,

(select a = 'b' , y = b from test1 where a = 'y') t2,

(select a = 'b' , z = b from test1 where a = 'z') t3

where t1.a = t2.a and t1.a = t2.a

union all

select t1.* , t2.y , t3.z from

(select a = 'c' , x = c from test1 where a = 'x') t1,

(select a = 'c' , y = c from test1 where a = 'y') t2,

(select a = 'c' , z = c from test1 where a = 'z') t3

where t1.a = t2.a and t1.a = t2.a

union all

select t1.* , t2.y , t3.z from

(select a = 'd' , x = d from test1 where a = 'x') t1,

(select a = 'd' , y = d from test1 where a = 'y') t2,

(select a = 'd' , z = d from test1 where a = 'z') t3

where t1.a = t2.a and t1.a = t2.a

union all

select t1.* , t2.y , t3.z from

(select a = 'e' , x = e from test1 where a = 'x') t1,

(select a = 'e' , y = e from test1 where a = 'y') t2,

(select a = 'e' , z = e from test1 where a = 'z') t3

where t1.a = t2.a and t1.a = t2.a

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

/*

表tb,數據以下:

項目種類 業績 提成

洗吹類  200   10

外賣      100   5

合計      300   15

轉換成:

項目種類 洗吹類 外賣 合計

業績      200     100   300

提成      10      5     15

*/

create table tb

(

項目種類 varchar(10),

業績     int,

提成     int

)

insert into tb(項目種類,業績,提成) values('洗吹類',200,10)

insert into tb(項目種類,業績,提成) values('外賣' ,100,5)

insert into tb(項目種類,業績,提成) values('合計' ,300,15)

go

select 項目種類,sum(洗吹類) as 洗吹類 , sum(外賣) as 外賣 , sum(合計) as 合計 from

(

select 項目種類 = '業績',

         洗吹類   = case when 項目種類 = '洗吹類' then 業績 else 0 end,

         外賣     = case when 項目種類 = '外賣'   then 業績 else 0 end,

         合計     = case when 項目種類 = '合計'   then 業績 else 0 end

from tb

union all

select 項目種類 = '提成' ,

         洗吹類   = case when 項目種類 = '洗吹類' then 提成 else 0 end,

         外賣     = case when 項目種類 = '外賣'   then 提成 else 0 end,

         合計     = case when 項目種類 = '合計'   then 提成 else 0 end

from tb

) m

group by 項目種類

order by 項目種類 desc

drop table tb

/*

項目種類 洗吹類      外賣        合計         

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

業績     200         100         300

提成     10          5           15

(所影響的行數爲 2 行)

*/

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

/*

數據庫中tb表格以下

 

月份    工資   福利 獎金

1月     100    200   300

2月     110    210   310

3月     120    220   320

4月     130    230   330

我想獲得的結果是

項目   1月    2月 3月 4月

工資   100    110 120 130

福利   200    210 220 230

獎金   300    310 320 330

就是說徹底把表格的行列顛倒,有點像那種旋轉矩陣,請問如何用sql 語句實現?

*/

if exists (select * from dbo.sysobjects

where id = object_id(N'[dbo].[p_zj]') and OBJECTPROPERTY(id, N'IsProcedure') = 1)

drop procedure [dbo].[p_zj]

GO

/*--行列互換的通用存儲過程(原著:鄒建):將指定的表,按指定的字段進行行列互換*/

create proc p_zj

       @tbname sysname, --要處理的表名

       @fdname sysname, --作爲轉換的列名

       @new_fdname sysname='' --爲轉換後的列指定列名

as

declare @s1 varchar(8000) , @s2 varchar(8000),

        @s3 varchar(8000) , @s4 varchar(8000),

        @s5 varchar(8000) , @i varchar(10)

select @s1 = '' , @s2 = '' , @s3 = '' , @s4 = '' , @s5 = '' , @i = '0'

select @s1 = @s1 + ',@' + @i + ' varchar(8000)',

       @s2 = @s2 + ',@' + @i + '=''' + case isnull(@new_fdname , '') when '' then ''

       else @new_fdname + '=' end + '''''' + name + '''''''',

       @s3 = @s3 + 'select @' + @i + + @i + '+'',['' + [' + @fdname +

       ']+'']=''+cast([' + name + '] as varchar) from [' + @tbname + ']',

       @s4 = @s4 + ',@' + @i + '=''select + @i,

       @s5 = @s5 + '+'' union all + @i,

       @i=cast(@i as int)+1

from syscolumns

where object_id(@tbname)=id and name<>@fdname

select @s1=substring(@s1,2,8000),

       @s2=substring(@s2,2,8000),

       @s4=substring(@s4,2,8000),

       @s5=substring(@s5,16,8000)

exec('declare ' + @s1 + 'select ' + @s2 + @s3 + 'select ' + @s4 + '

exec(' + @s5 + ')')

go

--用上面的存儲過程測試:

create table Test(月份 varchar(4), 工資 int, 福利 int, 獎金 int)

insert Test

select '1月',100,200,300 union all

select '2月',110,210,310 union all

select '3月',120,220,320 union all

select '4月',130,230,330

go

exec p_zj 'Test', '月份' , '項目'

drop table Test

drop proc p_zj

/*

項目   1月         2月         3月         4月         

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

福利   200         210         220         230

工資   100         110         120         130

獎金   300         310         320         330

(所影響的行數爲 3 行)

*/

/*

靜態寫法(SQL2005)

*/

--測試環境

create table Test(月份 varchar(4), 工資 int, 福利 int, 獎金 int)

insert Test

select '1月',100,200,300 union all

select '2月',110,210,310 union all

select '3月',120,220,320 union all

select '4月',130,230,330

go

--測試語句

SELECT * FROM

(

SELECT 考覈月份,月份,金額 FROM

     (SELECT 月份, 工資, 福利, 獎金 FROM Test) p

UNPIVOT

     (金額 FOR 考覈月份 IN (工資, 福利, 獎金))AS unpvt

) T

PIVOT

(MAX(金額) FOR 月份 in ([1月],[2月],[3月],[4月]))AS pt

--測試結果

/*

考覈月份 1月     2月      3月     4月

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

福利200210220230

工資100110120130

獎金300310320330

*/

--刪除環境

Drop table Test

相關文章
相關標籤/搜索