分享好友 数据库首页 频道列表

深入SQL中PIVOT 行列转换详解

其他教程  2015-10-26 09:560

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

得到的结果如下:

深入SQL中PIVOT 行列转换详解

(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

得到的结果如下:

深入SQL中PIVOT 行列转换详解

二、通过下面一个实例详细介绍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的行,完成这个查询。

查看更多关于【其他教程】的文章

展开全文
相关推荐
反对 0
举报 0
评论 0
图文资讯
热门推荐
优选好物
更多热点专题
更多推荐文章
去重复的sql(Oracle) 去重复的英文
1.利用group by 去重复2.可以利用下面的sql去重复,如下  1) select id,name,sex from (select a.*,row_number() over(partition by a.id,a.set order by name) su from test a ) where su=1  2)select id,name,sex from (select a.*,row_number() over(p

0评论2023-02-10893

Oracle SQL七次提速技巧
以下SQL执行时间按序号递减。1,动态SQL,没有绑定变量,每次执行都做硬解析操作,占用较大的共享池空间,若共享池空间不足,会导致其他SQL语句的解析信息被挤出共享池。create or replace procedure proc1as beginfor i in 1..100000 loop    execute imme

0评论2023-02-10755

SQL ORACLE case when函数用法
case when 用法(1)简单case函数:格式:  case 列名   when 条件值1 then 选项1  when 条件值1 then 选项2......  else 默认值 end例如:  select   case job_level  when '1' then '1111'  when '2' then '2222'   when '3' then '3333

0评论2023-02-10564

mysql下如何执行sql脚本 执行SQL脚本
1.编写sql脚本,假设内容如下:  create database dearabao;  use dearabao;  create table niuzi (name varchar(20));  保存脚本文件,假设我把它保存在F盘的hello world目录下,于是该文件的路径为:F:\hello world\niuzi.sql2.执行sql脚本,可以有2种方法: 

0评论2023-02-10699

MySQL 5.7版本sql_mode=only_full_group_by问题
用到GROUP BY 语句查询时com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Expression #2 of SELECT list is not in GROUP BY clause and contains nonaggregated column 'col_user_6.a.START_TIME' which is not functionally dependent on colu

0评论2023-02-10973

Oracle迁移到MySQL性能下降的注意点 oracle数据库迁移需要注意的问题
背景:最近有较多的客户系统由原来由Oracle改造到MySQL后出现了性能问题CPU 100%,或是后台的CRM系统复杂SQL在业务高峰的时候出现堆积导致业务故障。在我的记忆里面淘宝最初从Oracle迁移到MySQL期间也遇到了很多SQL的性能问题,记忆最为深刻的子查询,当初的

0评论2023-02-10580

ORACLE中通过SQL语句(alter table)来增加、删除、修改字段
1.添加字段:alter table  表名  add (字段  字段类型)  [ default  '输入默认值']  [null/not null]  ;2.添加备注:comment on column  库名.表名.字段名 is  '输入的备注';  如: 我要在ers_data库中  test表 document_type字段添加备注  comm

0评论2023-02-10584

MySQL与Oracle 差异比较之六触发器
触发器编号类别ORACLEMYSQL注释1创建触发器语句不同create or replace trigger TG_ES_FAC_UNIT  before insert or update or delete on ES_FAC_UNIT  for each rowcreate trigger `hs_esbs`.`TG_INSERT_ES_FAC_UNIT` BEFORE INSERT on `hs_esbs`.`es_fac_u

0评论2023-02-10914

mysql where条件:某时间字段为今天的sql语句
1.查询:注册时间为今天的所有用户数:select count(*) from customer where TO_DAYS(createtime) = TO_DAYS(NOW())2.获取当前时间到凌晨24点还有多长时间:(Java中可用于判断某时间是否为今天)final Calendar cal = Calendar.getInstance();    ca

0评论2023-02-10717

mysql中的sql
变量用户变量: 在用户变量前加@系统变量: 在系统变量前加@@运算符算术运算符有: +(加), -(减), * (乘), / (除) 和% (求模) 五中运算位运算符有:(位于), | (位或), ^ (位异或), ~ (位取反),(位右移),(位左移)比较运算符有: = (等于),(大于),(小于), = (大

0评论2023-02-10936

Oracle的HINT可以强制指定SQL的执行计划,比如选择索引、表的连接顺序以及表的连接方式等等。(转)
在Oracle中查看所有的表: select * from tab/dba_tables/dba_objects/cat; 看用户建立的表 :  select table_name from user_tables;  //当前用户的表 select table_name from all_tables;  //所有用户的表 select table_name from dba_tables;  //包

0评论2023-02-10857

Oracle sql 子字符串长度判断
Oracle sql 子字符串长度判断 select t.* from d_table t WHEREsubstr(t.col,1,1)='8' and instr(t.col,'/')0 and length(substr(t.col,1,instr(t.col,'/')))5; 字符串的前两位都是数字:select * from d_table t WHERE regexp_like(substr(t.col,1,2), '^[

0评论2023-02-10759

Oracle、MySql、Sql Server比对
MySql:廉价(部分免费):当前,MySQL採用双重授权(DualLicensed),他们是GPL和MySQLAB制定的商业许可协议。假设你在一个遵循GPL的***(开源)项目中使用MySQL,那么你能够遵循GPL协议免费使用MySQL。否则,你须要购买MySQLAB制定的那个商业许可协议。Windows $

0评论2023-02-10441

Oracle 存储过程,临时表,动态SQL测试
--创建事务级别的结果临时表create global temporary table tmp_yshy( c1 varchar2(100), c2 varchar2(100))on commit delete rows;--创建事务级别的存储sql语句的临时表create global temporary table tmp_sql( c1 varchar2(4000))on commit delete rows;测

0评论2023-02-10508

更多推荐