首页 > 代码库 > pivoting&Unpivoting

pivoting&Unpivoting

Pivoting技术是指一种可以把行转换为列的技术,Unpivoting可以看作是pivoting的反向操作,即将列旋转成为行

首先做一张测试表

  1. create table formatting(
  2. orderid int not null,
  3. orderdate date not null,
  4. empid int not null,
  5. custid vahrchar(10) not null,
  6. qty int not null,
  7. primary key (orderid,orderdate));


再插入一些测试数据:

  1. insert into formatting select 1,‘2010-01-02‘,3,‘A‘,10;
  2. insert into formatting select 2,‘2010-04-02‘,2,‘B‘,20;
  3. insert into formatting select 3,‘2010-05-02‘,1,‘A‘,30;
  4. insert into formatting select 4,‘2010-07-02‘,3,‘D‘,40;
  5. insert into formatting select 5,‘2011-01-02‘,4,‘A‘,20;
  6. insert into formatting select 6,‘2011-01-02‘,3,‘B‘,30;
  7. insert into formatting select 7,‘2011-01-02‘,1,‘C‘,40;
  8. insert into formatting select 8,‘2009-01-02‘,2,‘A‘,10;
  9. insert into formatting select 9,‘2009-01-02‘,3,‘B‘,20;


此时可以将表formatting看作是一张汇总表,比如网上商城的购物明细。
这份汇总表显示了订单号,订单日期,员工编号,消费者编号和订单数量。

要在此汇总表的基础上进一步统计每个消费者每年的订单数量,可能会想到用分组来获得结果,比如:

 


  1. select
     custid,year(orderdate) year,sum(qty) sum
  2. from formatting 
  3. group by custid,year(orderdate);

结果为:


这样子并不直观,如果可以通过旋转得到这样的结果,那就直观和清晰多了:




  1. select custid,
  2.         ifnull(sum(case when a=2009 then qty end),0) \"2009\",
  3.         ifnull(sum(case when a=2010 then qty end),0) \"2010\",
  4.         ifnull(sum(case when a=2011 then qty end),0) \"2011\"
  5. from (select custid,year(orderdate) a,qty
  6.     from formatting) p
  7. group by custid;

 

但是当旋转的元素非常多的说话,会产生较长的查询字符串
要缩短查询的字符串,可以预先产生一张矩阵表,包含每个要旋转列的属性:


  1. create table formatting_temp(
  2. orderyear int primary key,
  3. y2009 int,
  4. y2010 int,
  5. y2011 int);

 

  1. insert into formatting_temp select 2009,1,0,0;
  2. insert into formatting_temp select 2010,0,1,0;
  3. insert into formatting_temp select 2011,0,0,1;


将formatting和formatting_temp联接


  1. select custid,
  2.         sum(qty*y2009) "2009",
  3.         sum(qty*y2010) "2010",
  4.         sum(qty*y2011) "2011"
  5. from(select custid,YEAR(orderdate) a,qty
  6.     from formatting) o
  7. join formatting_temp p
  8. on o.a=p.orderyear
  9. group by custid;

 

 

 

 

【Unpivoting】

 

Unpivoting操作是将列转换成行,是Pivoting的逆操作


  1. create table unpivoting(
  2. custid vahrchar(10),
  3. y2009 int,
  4. y2010 int,
  5. y2011 int);



  1. insert into unpivoting
  2. select custid,
  3.         ifnull(sum(case when a=2009 then qty end),0) \"2009\",
  4.         ifnull(sum(case when a=2010 then qty end),0) \"2010\",
  5.         ifnull(sum(case when a=2011 then qty end),0) \"2011\"
  6. from (select custid,year(orderdate) a,qty
  7.     from formatting) p
  8. group by custid;


这里是把之前的formatting旋转之后的内容导入到unpivoting表中。

 

目前unpivoting表的内容



解决方案及思路:
解决这个问题需要将列旋转成为行,这里使用的技巧是对每行数据产生3个副本
每个副本产生一个需要旋转的列,这个过程可以通过如下的cross join来完成


  1. select * 
  2. from unpivoting,(select 2009 as orderyear
  3.                 union all select 2010
  4.                 union all select 2011) a;


结果为:





接下来,只需要根据orderyear列来去的对应旋转列的值就可以了

由于最后要得到




过滤掉qty=0的情况



因此这个问题的最终解决方案为:

  1. select custid,orderyear,qty
  2. from(
  3.     select custid,orderyear
  4.         case orderyear
  5.             when 2009 then y2009
  6.             when 2010 then y2010
  7.             when 2011 then y2011
  8.         end as qty
  9.     from unpivoting,(select 2009 as orderyear
  10.                 union all select 2010
  11.                 union all select 2011) a
  12.     ) b
  13. where qty <> 0;



 

pivoting&Unpivoting