SQL中PIVOT 行列转换

Posted Mr.石

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SQL中PIVOT 行列转换相关的知识,希望对你有一定的参考价值。

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

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


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

 

一、PIVOT实例

1. 建表

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

 
SQL 代码   复制
技术分享技术分享
 CREATE TABLE SalesByQuarter
技术分享
    (    year INT,    -- 年份
技术分享
        quarter CHAR(2),  -- 季度
技术分享
        amount MONEY  -- 总额
技术分享
    )
技术分享

 

2. 填入表数据

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

 
SQL 代码   复制
技术分享技术分享
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
技术分享
    END
技术分享

 

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

 

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

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

 
SQL 代码   复制
技术分享技术分享
    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语句查询实现相同的功能,但是你可以用更少的代码就实现,而且看起来更漂亮。
 

 
SQL 代码   复制
技术分享技术分享
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的过程

 

 
SQL 代码   复制
技术分享技术分享
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实例
 
 
SQL 代码   复制
技术分享
技术分享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的行,完成这个查询。









以上是关于SQL中PIVOT 行列转换的主要内容,如果未能解决你的问题,请参考以下文章

SQL Server中行列转换 Pivot UnPivot

sql server动态行列转换

SQL Fundamentals: 子查询 || 行列转换(PIVOT,UNPIVOT,DECODE),设置数据层次(LEVEL...CONNECT BY)

SQL Server pivot行列转换案例分析

sql server如何通过pivot对数据进行行列转换(进阶)

Sql2008的行列转换之行转列