暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

MSSQLSERVER PIVOT 行列转换详解

零壹旅途 2021-06-22
1854

T-SQL语句中,PIVOT运算符用于在列和行之间对数据进行旋转或透视转换,PIVOT命令可以实现数据表的列转行,同时执行聚合运算,UNPIVOT则与其相反,实现数据的行转列。

PIVOT通过将表达式某一列中的唯一值转换为输出中的多个列来旋转表值表达式,并在必要时对最终输出中所需的任何其余列值执行聚合。UNPIVOT与PIVOT执行相反的操作,将表值表达式的列转换为列值。

通俗简单的说:PIVOT就是行转列,UNPIVOT就是列转行

一、PIVOT实例

1. 建表

建立一个销售情况表,其中,year字段表示年份,quarter字段表示季度,amount字段表示销售额。quarter字段分别用Q1, Q2, Q3, Q4表示一、二、三、四季度。


CREATE
 TABLE
 SalesByQuarter

( 
year
 INT
, 
-- 年份

 
quarter 
CHAR
(2), 
-- 季度

 
amount MONEY 
-- 总额

)

2. 填入表数据

使用如下程序填入表数据。


SET
 NOCOUNT 
ON

 
DECLARE
 @
index
 INT

 
DECLARE
 @q 
INT

 
SET
 @
index
 = 0

 
DECLARE
 @
year
 INT

 
while (@
index
 < 30)

 
BEGIN

  
SET
 @
year
 = 2005 + (@
index
 % 4)

  
SET
 @q = (
CAST
((RAND() * 500) 
AS
 INT
) % 4) + 1

  
INSERT
 INTO
 SalesByQuarter 
VALUES
 (@
year
, 
'Q'
 + 
CAST
(@q 
AS
 CHAR
(1)), RAND() * 10000.00)

  
SET
 @
index
 = @
index
 + 1

3、如果我们要比较每年中各季度的销售状况,要怎么办呢?有以下两种方法:

(1)、使用传统Select的CASE语句查询

在SQL Server以前的版本里,将行级数据转换为列级数据就要用到一系列CASE语句和聚合查询。虽然这种方式让开发人员具有了对所返回数据进行高度控制的能力,但是编写出这些查询是一件很麻烦的事情。


SELECT
 year
 as
 年份

 
, 
sum
 (
case
 when
 quarter = 
'Q1'
 then
 amount 
else
 0 
end
) 一季度

 
, 
sum
 (
case
 when
 quarter = 
'Q2'
 then
 amount 
else
 0 
end
) 二季度

 
, 
sum
 (
case
 when
 quarter = 
'Q3'
 then
 amount 
else
 0 
end
) 三季度

 
, 
sum
 (
case
 when
 quarter = 
'Q4'
 then
 amount 
else
 0 
end
) 四季度

FROM
 SalesByQuarter 
GROUP
 BY
 year
 ORDER
 BY
 year
 DESC

(2)、使用PIVOT

由于SQL Server 2005有了新的PIVOT运算符,就不再需要CASE语句和GROUP BY语句了。(每个PIVOT查询都涉及某种类型的聚合,因此你可以忽略GROUP BY语句。)PIVOT运算符让我们能够利用CASE语句查询实现相同的功能,但是你可以用更少的代码就实现,而且看起来更漂亮。


SELECT
 year
 as
 年份, Q1 
as
 一季度, Q2 
as
 二季度, Q3 
as
 三季度, Q4 
as
 四季度 
FROM
 SalesByQuarter PIVOT (
SUM
 (amount) 
FOR
 quarter 
IN
 (Q1, Q2, Q3, Q4) ) 
AS
 P 
ORDER
 BY
 YEAR
 DESC

二、通过下面一个实例详细介绍PIVOT的过程


SELECT
 [星期一],[星期二],[星期三],[星期四],[星期五],[星期六],[星期日]
--这里是PIVOT第三步(选择行转列后的结果集的列)这里可以用“*”表示选择所有列,也可以只选择某些列(也就是某些天)

FROM
 WEEK_INCOME 
--这里是PIVOT第二步骤(准备原始的查询结果,因为PIVOT是对一个原始的查询结果集进行转换操作,所以先查询一个结果集出来)这里可以是一个select子查询,但为子查询时候要指定别名,否则语法错误

PIVOT

(

 
SUM
(INCOME) 
for
 [week] 
in
([星期一],[星期二],[星期三],[星期四],[星期五],[星期六],[星期日])
--这里是PIVOT第一步骤,也是核心的地方,进行行转列操作。聚合函数SUM表示你需要怎样处理转换后的列的值,是总和(sum),还是平均(avg)还是min,max等等。例如如果week_income表中有两条数据并且其week都是“星期一”,其中一条的income是1000,另一条income是500,那么在这里使用sum,行转列后“星期一”这个列的值当然是1500了。后面的for [week] in([星期一],[星期二]...)中 for [week]就是说将week列的值分别转换成一个个列,也就是“以值变列”。但是需要转换成列的值有可能有很多,我们只想取其中几个值转换成列,那么怎样取呢?就是在in里面了,比如我此刻只想看工作日的收入,在in里面就只写“星期一”至“星期五”(注意,in里面是原来week列的值,"以值变列")。总的来说,SUM(INCOME) for [week] in([星期一],[星期二],[星期三],[星期四],[星期五],[星期六],[星期日])这句的意思如果直译出来,就是说:将列[week]值为"星期一","星期二","星期三","星期四","星期五","星期六","星期日"分别转换成列,这些列的值取income的总和。

)TBL
--别名一定要写

三.UNPIVOT

很明显,UN这个前缀表明了,它做的操作是跟PIVOT相反的,即列转行。UNPIVOT操作涉及到以下三个逻辑处理阶段。

1,生成副本
2,提取元素
3,删除带有NULL的行

UNPIVOT实例


CREATE
 TABLE
 pvt (VendorID 
int
, Emp1 
int
, Emp2 
int
,

 
Emp3 
int
, Emp4 
int
, Emp5 
int
);

GO

INSERT
 INTO
 pvt 
VALUES
 (1,4,3,5,4,4);

INSERT
 INTO
 pvt 
VALUES
 (2,4,1,5,5,5);

INSERT
 INTO
 pvt 
VALUES
 (3,4,3,5,4,4);

INSERT
 INTO
 pvt 
VALUES
 (4,4,2,5,5,4);

INSERT
 INTO
 pvt 
VALUES
 (5,5,1,5,5,5);

GO

--Unpivot the table.

SELECT
 VendorID, Employee, Orders

FROM

 
(
SELECT
 VendorID, Emp1, Emp2, Emp3, Emp4, Emp5

 
FROM
 pvt) p

UNPIVOT

 
(Orders 
FOR
 Employee 
IN

  
(Emp1, Emp2, Emp3, Emp4, Emp5)

)
AS
 unpvt;

GO

上面UNPIVOT实例的分析

UNPIVOT的输入是左表表达式P,第一步,先为P中的行生成多个副本,在UNPIVOT中出现的每一列,都会生成一个副本。因为这里的IN子句有5个列名称,所以要为每个来源行生成5个副本。结果得到的虚拟表中将新增一个列,用来以字符串格式保存来源列的名称(for和IN之间的,上面例子是 Employee )。第二步,根据新增的那一列中的值从来源列中提取出与列名对应的行。第三步,删除掉结果列值为null的行,完成这个查询。


文章转载自零壹旅途,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论