Pivot研究

      今天學習了SQL2005的PIVOT,PIVOT 關係運算符對錶值表達式進行操作以獲得另一個表。PIVOT 通過將表達式某一列中的唯一值轉換爲輸出中的多個列來轉換表值表達式,並在必要時對最終輸出中所需的任何其餘的列值執行聚合。記得我們在SQL2000中要用聚合和CASE語句完成一個行列轉換吧,特別當待轉成列的數據不定時,我們往往構造動態SQL,然後用EXEC來運行。

環境準備: (下面我引用大烏龜的代碼來說明)

問題:假設有張學生成績表(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

 

 

一.我們先看SQL2000中的處理方法:

   在SQL2000中行列轉換:我們對上例程把【課程】轉列顯示,並把【分數】當對應列值。我們分兩種情況來討論:

 

 

1、當【課程】的列值固定就[語文] [數學] [物理] 三種情況

 

 

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 姓名

 

 

 

2、當【課程】的列值不固定時就運用動態SQL ,其實也就是構造一個sum(CASE WHEN ...)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)

 

 

 

二.在SQL2005中,我們有更簡單的表達方式(PIVOT)。

 

 

1.先來看看2005 的FROM 子句的定義:

 

[ FROM { <table_source> } [ ,...n ] ]

<table_source> ::= 
   { <pivoted_table> }

<pivoted_table> ::=
          table_source PIVOT <pivot_clause> table_alias

<pivot_clause> ::=
        ( aggregate_function ( value_column ) 
        FOR pivot_column 
        IN ( <column_list> ) 
        )

<column_list> ::=
         column_name [ , ... ]

 

 

pivot_column 和value_column 是PIVOT運算符使用的組合列。

 

PIVOT遵循以下過程獲得輸出結果集:

() 對分組列的input_table 執行GROUP BY ,爲每個組生成一個輸出行。

() 輸出行中的分組列獲得input_table 中該組的對應列值。

() 通過執行以下操作,爲每個輸出行生成列列表中的列的值:

     a 針對pivot_column,對上一步在GROUP BY 中生成的行另外進行分組。

     b 對於column_list中的每個輸出列,選擇滿足以下條件的子組:

pivot_column = CONVERT(<data type of pivot_column>, 'output_column'),針對此子組上的aggregate_functionvalue_column 求值,其結果作爲相應的output_column 的值返回。如果該子組爲空,SQL Server 將爲該output_column生成空值。如果聚合函數是 COUNT ,且子組爲空,則返回零(0)

      接着我們利用我們開頭的例子來理解一下這個FROM 子句,很顯然我們的【分數】對應上面的value_column, 我們還假定列會下固定爲這三項,那麼列【課程】對應上面的pivot_column, 進而我們應該得出[語文] [數學] [物理]column_name 即我們的輸出列,最後我們只要構造一下table_source 就可以了,如何構造這個table_source ,顯然pivot_column value_column 應該包含在其中,其它就應該是你想要分組的列啦.

 

    我們來總結一下:這個FROM 子句是基於 table_source  pivot_column 進行透視,table_source  pivot_column  value_column 列之外的列被稱爲透視運算符的組合列, PIVOT 是對輸入表執行組合列的分組操作,併爲每個組返回一行,好,我們試着寫出這個SQL 

靜態SQL:

select * from (select * from tb) a pivot (max(分數) for 課程 in (語文,數學,物理)) b

動態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')

三.下面我們用例子分析下PIVOT

1.原始數據:

 

 

姓名         課程         分數          測試
---------- ---------- ----------- ----------
張三         語文         74          測試1
張三         數學         83          測試2
張三         物理         93          測試3
李四         語文         74          測試4
李四         數學         84          測試5
李四         物理         94          測試6

 

 

執行語句:

select * from tb pivot (max(分數) for 課程in (語文,數學,物理)) b

結果:

姓名         測試         語文          數學          物理
---------- ---------- ----------- ----------- -----------
張三         測試1        74          NULL        NULL
張三         測試2        NULL        83          NULL
張三         測試3        NULL        NULL        93
李四         測試4        74          NULL        NULL
李四         測試5        NULL        84          NULL
李四         測試6        NULL        NULL        94

(6 行受影響)

2.原始數據:

姓名         課程         分數          測試
---------- ---------- ----------- ----------
張三         語文         74          測試1
張三         數學         83          測試1
張三         物理         93          測試3
李四         語文         74          測試3
李四         數學         84          測試5
李四         物理         94          測試5

 

 

執行語句:

select * from tb pivot (max(分數) for 課程in (語文,數學,物理)) b

結果:

 名         測試         語文          數學          物理
---------- ---------- ----------- ----------- -----------
張三         測試1        74          83          NULL
李四         測試3        74          NULL        NULL
張三         測試3        NULL        NULL        93
李四         測試5        NULL        84          94

(4 行受影響)

看出來了吧?先基於姓名和測試分組,再按照課程創建列,填如對應的分數,沒有對應數據的補NULL。

這樣,在上面兩種源數據的情況下,最初我們想要得到的數據也很容易查得。

 

 

select * from (SELECT [姓名],[課程],[分數]FROM [tb])a

   pivot (max(分數) for 課程in (語文,數學,物理)) b

 

 

姓名         語文          數學          物理
---------- ----------- ----------- -----------
李四         74          84          94
張三         74          83          93

(2 行受影響)

發佈了34 篇原創文章 · 獲贊 4 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章