Pivot 和 Unpivot


在TSQL中,使用Pivot和Unpivot運算符將一個關系表轉換成另外一個關系表,兩個命令實現的操作是“相反”的,但是,pivot之后,不能通過unpivot將數據還原。這兩個運算符的操作數比較復雜,記錄一下自己的總結,以后用到時,作為參考。

一,Pivot用法

Pivot旋轉的作用,是將關系表(table_source)中的列(pivot_column)的值,轉換成另一個關系表(pivot_table)的列名:

table_source
pivot
(
  aggregation_function(aggregated_column)
  for pivot_column in ([pivot_column_value_list])
) as pivot_table_alias

透視操作的處理流程是:

  1. 對pivot_column和 aggregated_column的其余column進行分組,即,group by other_columns;
  2. 當pivot_column值等於某一個指定值,計算aggregated_column的聚合值;

在使用透視命令時,需要注意:

  • pivot將table_source旋轉成透視表(pivot_table)之后,不能再被引用
  • pivot_column的列值,必須使用中括號([])界定符
  • 必須顯式命名pivot_table的別名

1,創建示例數據

use tempdb
go 

drop table if exists dbo.usr
go

create table dbo.usr
(
    name varchar(10),
    score int,
    class varchar(8)
)
go

insert into dbo.usr
values('a',20,'math'),('b',21,'math'),('c',22,'phy'),('d',23,'phy')
,('a',22,'phy'),('b',23,'phy'),('c',24,'math'),('d',25,'math')
go
View Code

2,對name進行分組,對score進行聚合,將class列的值轉換為列名

select p.name,p.math,p.phy
from dbo.usr u
pivot
(
    sum(score)
    for class in([math],[phy]) 
) as p

3,pivot的等價寫法:使用case when語句實現

pivot命令的執行流程很簡單,使用caseh when子句實現pivot的功能

select u.name,
    sum(case when u.class='math' then u.score else null end) as math,
    sum(case when u.class='phy' then u.score else null end) as phy
from dbo.usr u
group by u.name

使用group by子句對name列分組,使用 case when 語句將pivot_column的列值作為列名返回,並對aggregated_column計算聚合值。

4,動態Pivot寫法

靜態pivot寫法的弊端是:如果pivot_column的列值發生變化,靜態pivot不能對新增的列值進行透視,變通方法是使用動態sql,拼接列值

Script1,使用case-when子句實現

declare @sql nvarchar(max)
declare @columnlist nvarchar(max)

set @columnlist=N''

;with cte as
(
select distinct class
from dbo.usr
)
select @columnlist+='sum(case when u.class='''+cast(class as varchar(10))+N''' then u.score else null end) as ['+cast(class as varchar(10))+N'],'
from cte

select @columnlist=SUBSTRING(@columnlist,1,len(@columnlist)-1)

select @sql=
N'select u.name,'
    +@columnlist
+N'from dbo.usr u
group by u.name'

exec(@sql)
View Code

Script2,使用pivot子句實現

declare @sql nvarchar(max)
declare @classlist nvarchar(max)

set @classlist=N''

;with cte as
(
    select distinct class
    from dbo.usr
)
select @classlist+=N'['+cast(class as varchar(11))+N'],'
from cte

select     @classlist=SUBSTRING(@classlist,1,len(@classlist)-1)

select @sql=N'select p.name,'+@classlist+
N' from dbo.usr u
PIVOT
(
    sum(score) 
    for class in('+@classlist+N')
) as p'

exec (@sql)
View Code

二,Unpivot用法

unpivot是將列名轉換為列值,列名做為列值,因此,會新增兩個column:一個column用於存儲列名,一個column用於存儲列值

table_soucr
unpivot
(
newcolumn_store_unpivotcolumn_name for 
newcolumn_store_unpivotcolumn_value in (unpivotcolumn_name_list)  
)

逆透視(unpivot)的處理流程是:

  1. unpivotcolumn_name_list是逆透視列的列表,其列值是相兼容的,能夠存儲在一個column中
  2. 保持其他列(除unpivotcolumn_name_list之外的所有列)的列值不變
  3. 依次將unpivotcolumn的列名存儲到newcolumn_store_unpivotcolumn_name字段中,將unpivotcolumn的列值存儲到newcolumn_store_unpivotcolumn_value字段中

1,創建示例數據

CREATE TABLE dbo.Venders 
(
    VendorID int, 
    Emp1 int, 
    Emp2 int,  
    Emp3 int, 
    Emp4 int, 
    Emp5 int
);  
GO 
 
INSERT INTO dbo.Venders VALUES (1,4,3,5,4,4);  
INSERT INTO dbo.Venders VALUES (2,4,1,5,5,5);  
INSERT INTO dbo.Venders VALUES (3,4,3,5,4,4);  
INSERT INTO dbo.Venders VALUES (4,4,2,5,5,4);  
INSERT INTO dbo.Venders VALUES (5,5,1,5,5,5);  
GO 
View Code

2,unpivot用法示例

將Emp1, Emp2, Emp3, Emp4, Emp5的列名和列值存儲到字段:Employee和Orders中

SELECT VendorID, Employee, Orders  
FROM dbo.Venders as p 
UNPIVOT  
(Orders FOR Employee IN   
      (Emp1, Emp2, Emp3, Emp4, Emp5)  
)AS unpvt;  
GO 

3,unpivot可以使用union all來實現

select VendorID, 'Emp1' as Employee, Emp1 as Orders
from dbo.Venders
union all 
select VendorID, 'Emp2' as Employee, Emp2 as Orders
from dbo.Venders
union all 
select VendorID, 'Emp3' as Employee, Emp3 as Orders
from dbo.Venders
union all
select VendorID, 'Emp4' as Employee, Emp4 as Orders
from dbo.Venders
union all
select VendorID, 'Emp5' as Employee, Emp5 as Orders
from dbo.Venders
View Code

4,動態unpivot的實現,使用動態sql語句

聰明如你,很容易實現,代碼就不貼了....

三,性能討論

pivot和unpivot的性能不是很好,不要用來處理海量的數據

 

參考文檔:

Using PIVOT and UNPIVOT


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM