现在的位置: 首页 > 综合 > 正文

[学习SQL SERVER 2005系列]感受新功能一:PIVOT

2014年03月26日 ⁄ 综合 ⁄ 共 6344字 ⁄ 字号 评论关闭

工具的升级,我以为得先看看这个工具在哪些功能上得到加强,今天我们就看看SQL2005这个PIVOT吧。PIVOT 关系运算符对表值表达式进行操作以获得另一个表。PIVOT 通过将表达式某一列中的唯一值转换为输出中的多个列来转换表值表达式,并在必要时对最终输出中所需的任何其余的列值执行聚合。记得我们在SQL2000中要用聚合和CASE语句完成一个行列转换吧,特别当待转成列的数据不定时,我们往往构造动态SQL,然后用EXEC来运行。
环境准备:

  1. ------------------------------------ 
  2.     -- Author:  happyflsytone   
  3.     -- Version:V1.001   
  4.     -- Date:2008-09-18 10:20:53 
  5.     ------------------------------------ 
  6.     
  7.     -- Test Data: ta 
  8.     IF OBJECT_ID('ta') IS NOT NULL  
  9.         DROP TABLE ta 
  10.     ; 
  11.     CREATE  TABLE ta(id INT,col1 Nvarchar(2),col2 Nvarchar(2),col3 Nvarchar(4),col4 INT) 
  12.     ; 
  13.     INSERT INTO ta 
  14.     SELECT 1,'HN','CS','abc',1 UNION ALL 
  15.     SELECT 2,'HN','CS','abcd',2 UNION ALL 
  16.     SELECT 3,'HN','CD','abcd' ,3UNION ALL 
  17.     SELECT 4,'HN','HY','ae' ,4 
  18.     ;

我们先来回顾SQL2000 的行列转换, 比如我们对上例程把col3 转列显示,并把col4 的和当对应列值。我们分两种情况来讨论:

     一、当col3 的列值固定就是'abc','abcd','ae' 三种情况

  1. SELECT  
  2.         col1, 
  3.         col2, 
  4.         [abc]   = SUM(CASE WHEN col3 = 'abc' THEN col4 ELSE 0 END), 
  5.         [abcd]  = SUM(CASE WHEN col3 = 'abcd' THEN col4  ELSE 0 END), 
  6.         [ae]    = SUM(CASE WHEN col3 = 'ae' THEN col4  ELSE 0 END) 
  7.     FROM ta 
  8.     GROUP BY col1,col2

 /*

  col1 col2 abc         abcd        ae

  ---- ---- ----------- ----------- -----------

 HN   CD   0           3           0

HN   CS   1           2           0

HN   HY   0           0           4

 (3 行受影响)

  */

    二、当col3 的列值不固定时就运用动态SQL ,其实也就是构造一个sum(CASE WHEN ...)SQL 字符串

  1. DECLARE @s varchar(8000
  2.     SELECT @s = isnull(@s+', 
  3.         ','') +'['+col3+'] = SUM(CASE WHEN col3 = '''+col3+''' THEN col4 ELSE 0 END)' 
  4.     FROM ( SELECT distinct col3 FROM ta) a 
  5.     SET @s = 'SELECT  
  6.         col1, 
  7.         col2, 
  8.         '+@s + '  
  9.     FROM ta  
  10.     GROUP BY  
  11.         col1,col2' 
  12.     EXEC(@s)

  /*

  col1 col2 abc         abcd        ae

  ---- ---- ----------- ----------- -----------

  HN   CD   0           3           0

  HN   CS   1           2           0

HN   HY   0           0           4

  

  (3 行受影响)

  */

     我们先输入这个@S 看看是什么东东,只要加上print @s

  1. SELECT  
  2.         col1, 
  3.         col2, 
  4.         [abc] = SUM(CASE WHEN col3 = 'abc' THEN col4 ELSE 0 END), 
  5.         [abcd] = SUM(CASE WHEN col3 = 'abcd' THEN col4 ELSE 0 END), 
  6.         [ae] = SUM(CASE WHEN col3 = 'ae' THEN col4 ELSE 0 END)  
  7.     FROM ta  
  8.     GROUP BY  
  9.         col1,col2

其实就是上面我们构造的固定列值的SQL 嘛。

    好,现在们开始在2005 中实现这个功能,先来看看2005 的FROM 子句的定义( 关于如何看这个定义请参照SQL2005 的文档约定及Transate-SQL 语法约定) :

  [ FROM { <table_source> } [ ,...n ] ] 

  <table_source> ::= 

   {

       <pivoted_table> 

  }

  <pivoted_table> ::=

          table_source PIVOT <pivot_clause> table_alias

   

   <pivot_clause> ::=

        ( aggregate_function ( value_column ) 

        FOR pivot_column 

        IN ( <column_list> ) 

   ) 

<column_list> ::=

         column_name [ , ... ]

pivot_column 和 value_column 是 PIVOT 运算符使用的组合列。PIVOT 遵循以下过程获得输出结果集:

对分组列的 input_table 执行 GROUP BY ,为每个组生成一个输出行。

输出行中的分组列获得 input_table 中该组的对应列值。

通过执行以下操作,为每个输出行生成列列表中的列的值:

针对 pivot_column ,对上一步在 GROUP BY 中生成的行另外进行分组。

对于 column_list 中的每个输出列,选择满足以下条件的子组:

pivot_column = CONVERT(<data type of pivot_column>, 'output_column')

针对此子组上的 aggregate_function 对 value_column 求值,其结果作为相应的 output_column 的值返回。如果该子组为空,SQL Server 将为该 output_column 生成空值。如果聚合函数是 COUNT ,且子组为空,则返回零 (0) 。 

    接着我们利用我们开头的例子来理解一下这个FROM 子句,很显然我们的col4 对应上面的value_column, 我们还假定列会下固定为这三项,那么列 col3 对应上面的pivot_column, 进而我们应该得出[abc],[abcd],[ae] 是column_name 即我们的输出列,最后我们只要构造一下table_source 就可以了,如何构造这个table_source ,显然pivot_column 和 value_column 应该包含在其中,其它就应该是你想要分组的列啦.

   我们来总结一下:这个FROM 子句是基于 table_source 对 pivot_column 进行透视,table_source 中 pivot_column 和 value_column 列之外的列被称为透视运算符的组合列, 而PIVOT 是对输入表执行组合列的分组操作,并为每个组返回一行,好,我们试着写出这个SQL :

  1. SELECT col1,col2,[abc],[abcd],[ae] 
  2.     FROM  
  3.        (SELECT col1,col2,col3,col4 
  4.         FROM ta ) p 
  5.     PIVOT 
  6.        ( SUM (col4)  
  7.         FOR col3 IN ([abc],[abcd],[ae]) 
  8.        )AS unpvt

  我们执行一下看看结果:

   /*

   col1 abc         abcd        ae

   ---- ----------- ----------- -----------

  HN   1           NULL        NULL

   HN   NULL        2           NULL

  HN   NULL        3           NULL

  HN   NULL        NULL        4

    

(4 行受影响)

*/

如果我们去掉这些NULL 那么可以这样:

  1. SELECT col1,col2,ISNULL([abc],0) AS [ABC],ISNULL([abcd],0) AS [ABCD],ISNULL([ae],0) AS [AE] 
  2.     FROM  
  3.        (SELECT col1,col2,col3,col4 
  4.         FROM ta ) p 
  5.     PIVOT 
  6.        ( SUM (col4)  
  7.         FOR col3 IN ([abc],[abcd],[ae]) 
  8. .       )AS unpvt

   /*

   

   col1 col2 ABC         ABCD        AE

  ---- ---- ----------- ----------- -----------

   HN   CD   0           3           0

   HN   CS   1           2           0

  HN   HY   0           0           4

    

  (3 行受影响)

*/

当然在2005 中列值不固定时也要用到动态SQL ,我们把这个例子完成如下:

  1. DECLARE @s VARCHAR(1000
  2.     SELECT @s = isnull(@s + ',','')+ '['+ltrim(COL3)+']' 
  3.     FROM (SELECT DISTINCT col3 FROM ta ) a 
  4.     
  5.     EXEC('SELECT col1,col2,'+@s+' 
  6.     FROM  
  7.        (SELECT col1,col2,COL3,COL4 
  8.        FROM TA) p 
  9.     PIVOT 
  10.        ( SUM (COL4)  
  11.         FOR COL3 IN ('+@s+'
  12.        )AS unpvt')

  /*

  col1 col2 abc         abcd        ae

   ---- ---- ----------- ----------- -----------

   HN   CD   NULL        3           NULL

   HN   CS   1           2           NULL

  HN   HY   NULL        NULL        4

   

   (3 行受影响)

   */

最后我们再完成一个table_source 是多表关联的例子,准备数据如下:

  1. -- Test Data: ta 
  2.     If object_id('ta'is not null  
  3.         Drop table ta 
  4.     ; 
  5.     
  6.     Create table ta(id int,省 nvarchar(2),市 nvarchar(2),具体货品 nvarchar(4)) 
  7.     ; 
  8.     Insert into ta 
  9.     select 1,'HN','CS','abc' union all 
  10.     select 2,'HN','CS','abcd' union all 
  11.     select 3,'HN','CD','abcd' union all 
  12.     select 4,'HN','HY','ae'  
  13.     ; 
  14.     -- Test Data: tb 
  15.     If object_id('tb'is not null  
  16.         Drop table tb 
  17.     ; 
  18.     Create table tb(编号 int,具体货品 nvarchar(5),大类别 int
  19.     ; 
  20.     Insert into tb 
  21.     select 1,'abc',1 union all 
  22.     select 2,'abcd',2 union all 
  23.     select 3,'abcde',1 union all 
  24.     select 4,'ae',3  
  25.     Go 
  26.     --Start 
  27.     
  28.     -----2005写法 
  29.     select @s = isnull(@s + ',','')+ '['+ltrim(大类别)+']' 
  30.     from (select distinct top 100 percent  大类别 from tb order by 大类别 ) a 
  31.     
  32.     exec('SELECT 省,市,'+@s+' 
  33.     FROM  
  34.        (SELECT 省,市,大类别,a.编号 
  35.        FROM ta a left join tb b on a.具体货品 = b.具体货品) p 
  36.     PIVOT 
  37.        ( COUNT (编号)  
  38.         FOR 大类别 IN ('+@s+'
  39.        )AS unpvt') 
  40.  --Result:

/*

   

   (3 行受影响)

   

   省    市    1           2           3

   ---- ---- ----------- ----------- -----------

  HN   CD   0           1           0

HN   CS   1           1           0

HN   HY   0           0           1

(3 行受影响)

 

*/

--End 

好,我们对2005 的PIVOT 这个新功能的学习就到这儿了,多练习就可以熟练的使用这个PIVOT 用法。

抱歉!评论已关闭.