Pivot 和 Unpivot

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
复制代码

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)
复制代码

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)
复制代码

二,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,创建示例数据

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来实现

View Code

4,动态unpivot的实现,使用动态sql语句

聪明如你,很容易实现,代码就不贴了....

三,性能讨论

pivot和unpivot的性能不是很好,不要用来处理海量的数据

 

参考文档:

Using PIVOT and UNPIVOT

posted @ 2022-07-08 17:38  KJXY  阅读(632)  评论(0)    收藏  举报