zoukankan      html  css  js  c++  java
  • [转]SQL server 2005 PIVOT运算符的使用

    本文转自:http://blog.csdn.net/robinson_0612/article/details/5385117

    PIVOT,UNPIVOT运算符是SQL server 2005支持的新功能之一,主要用来实现行到列的转换。本文主要介绍PIVOT运算符的操作,以及如何实现动态PIVOT的行列转换。

           关于UNPIVOT及SQL server 2000下的行列转换请参照本人的其它文章。

    一、PIVOT的语法
    1. SELECT  
    2.   [non-pivoted column], -- optional   
    3.   [additional non-pivoted columns], -- optional   
    4.   [first pivoted column],  
    5.   [additional pivoted columns]  
    6. FROM (  
    7.   SELECT query producing sql data for pivot  
    8.   -- select pivot columns as dimensions and   
    9.   -- value columns as measures from sql tables   
    10. ) AS TableAlias  
    11. PIVOT  
    12. (  
    13.  
    14.   <aggregation function>(columnfor aggregation or measure column) -- MIN,MAX,SUM,etc   
    15.  
    16.   FOR []  
    17.   IN (  
    18.     [first pivoted column], ..., [last pivoted column]  
    19.   )  
    20. ) AS PivotTableAlias  
    21. ORDERBY clause – optional 
    SELECT 
      [non-pivoted column], -- optional 
      [additional non-pivoted columns], -- optional 
      [first pivoted column], 
      [additional pivoted columns] 
    FROM ( 
      SELECT query producing sql data for pivot 
      -- select pivot columns as dimensions and 
      -- value columns as measures from sql tables 
    ) AS TableAlias 
    PIVOT 
    ( 
    
      <aggregation function>(column for aggregation or measure column) -- MIN,MAX,SUM,etc 
    
      FOR [] 
      IN ( 
        [first pivoted column], ..., [last pivoted column] 
      ) 
    ) AS PivotTableAlias 
    ORDER BY clause – optional
    
    

    二、PIVOT的使用例子

    1. 静态PIVOT的用法        为演示,从NorthWind数据库中提取一些记录生成新的Orders表,然后使用PIVOT将行转换到列。

    1. USE tempdb 
    2. GO 
    3. SELECTYEAR(OrderDate) AS [Year
    4.        ,CustomerID  
    5.        ,od.Quantity 
    6. INTO dbo.Orders        
    7. FROM NorthWind..Orders AS
    8.     JOIN NorthWind..[Order Details] AS od 
    9.         ON o.OrderID = od.OrderID 
    10. WHERE o.CustomerID IN ('BONAP','BOTTM','ANTON'
    11. SELECT CustomerID 
    12.     ,[1996],[1997],[1998] 
    13. FROM dbo.Orders 
    14. PIVOT ( 
    15.        SUM(Quantity) 
    16.        FOR [Year] IN ([1996],[1997],[1998]) 
    17.    )x 
    18. /*              
    19. TSQL中pivot的结构: 
    20.     ●  用于生成pivot数据源的源表,作为一个输入表 
    21.     ●  pivot表 
    22.     ●  聚合列及透视列的选择 
    23.  
    24. TSQL中pivot的实现: 
    25. 1->上例中Orders表相当于是一个输入表。包含了CustomerID,[Year],Quantity 三个列。 
    26.    Year是透视列,用于生成维度。 
    27.    pivot首先将聚合列之外的列进行分组,并对其实现聚合。本列中则是对聚合列Quantity之外的列先实现分组, 
    28.    即对CustomerID,Year进行分组,并对其Quantity实现聚合,相当于先做如下处理: 
    29. */                  
    30. SELECT CustomerID 
    31.        ,[Year
    32.        ,SUM(Quantity) AS Total 
    33. FROM dbo.Orders 
    34. GROUPBY CustomerID 
    35.        ,[Year
    36. ORDERBY CustomerID             
    37.  
    38. /*  Result:         
    39. CustomerID Year        Total 
    40. ---------- ----------- -----------  
    41. ANTON      1996        24 
    42. ANTON      1997        295 
    43. ANTON      1998        40 
    44. BONAP      1996        181 
    45. BONAP      1997        486 
    46. BONAP      1998        313 
    47. BOTTM      1996        81 
    48. BOTTM      1997        454 
    49. BOTTM      1998        421 
    50. */ 
    51. /* 
    52. 2->pivot根据FOR [Year] IN子句中的值,在结果集中来建立对应的新列,本例中即是列,, 
    53.    对于新列,,中的取值,取中间结果集中与之相对应的值。 
    54.    如对于客户ANTON,1996列中的值就选择中间结果中对应的Total值,同理列中为。 
    55.    并将中间结果pivot表命名为x。 
    56.     
    57. 3->最外层的SELECT语句从pivot表生成最终结果,此处因Orders表仅有列,故直接将结果用一个SELECT返回,有嵌套的SELECT参照下例。 
    58.    
    59. --结果:    
    60. CustomerID 1996        1997        1998 
    61. ---------- ----------- ----------- -----------  
    62. ANTON      24          295         40 
    63. BONAP      181         486         313 
    64. BOTTM      81          454         421 
    65. */         以下是为输入表多于一列的例子,数据来源于SQL server 2005的AdventureWorks,其实现的原理同上。 
    66. SELECT *   
    67. FROM
    68.     SELECTYEAR(DueDate) [Year
    69.            ,CASEMONTH(DueDate) 
    70.             WHEN 1 THEN'January'  
    71.             WHEN 2 THEN'February' 
    72.             WHEN 3 THEN'March' 
    73.             WHEN 4 THEN'April' 
    74.             WHEN 5 THEN'May' 
    75.             WHEN 6 THEN'June' 
    76.             WHEN 7 THEN'July' 
    77.             WHEN 8 THEN'August' 
    78.             WHEN 9 THEN'September' 
    79.             WHEN 10 THEN'October' 
    80.             WHEN 11 THEN'November' 
    81.             WHEN 12 THEN'December' 
    82.            ENDas [Month
    83.            ,ProductID 
    84.            ,OrderQty 
    85.     FROM Production.WorkOrder 
    86. )WorkOrder 
    87.     PIVOT ( 
    88.            SUM(OrderQty) 
    89.            FOR [Month] IN ([January],[February],[March],[April],[May],[June],[July],[August],[September],[October],[November],[December]) 
    90.           )x 
    91. ORDERBY [Year], ProductID   
    92. --Result: 末尾部分省略  
    93. /*   
    94. Year        ProductID   January     February    March       April       May         June        July        August       
    95. ----------- ----------- ----------- ----------- ----------- ----------- ----------- ----------- ----------- -----------   
    96. 2002        3           8480        16870       12960       9530        19390       14170       26200       35870              
    97. 2002        316         1842        3704        2910        2252        4738        3496        7624        10778           
    98. 2002        324         1842        3704        2910        2252        4738        3496        7546        10600             
    99. 2002        327         921         1852        1455        1126        2369        1748        3773        5300               
    100. 2002        328         414         1048        872         458         1272        992         1786        2632          
    101. */ 
    USE tempdb
    GO
    SELECT YEAR(OrderDate) AS [Year]
           ,CustomerID 
           ,od.Quantity
    INTO dbo.Orders       
    FROM NorthWind..Orders AS o
        JOIN NorthWind..[Order Details] AS od
            ON o.OrderID = od.OrderID
    WHERE o.CustomerID IN ('BONAP','BOTTM','ANTON')
    SELECT CustomerID
    	,[1996],[1997],[1998]
    FROM dbo.Orders
    PIVOT (
    	   SUM(Quantity)
    	   FOR [Year] IN ([1996],[1997],[1998])
       )x
    /*			   
    TSQL中pivot的结构:
        ●  用于生成pivot数据源的源表,作为一个输入表
        ●  pivot表
        ●  聚合列及透视列的选择
    
    TSQL中pivot的实现:
    1->上例中Orders表相当于是一个输入表。包含了CustomerID,[Year],Quantity 三个列。
       Year是透视列,用于生成维度。
       pivot首先将聚合列之外的列进行分组,并对其实现聚合。本列中则是对聚合列Quantity之外的列先实现分组,
       即对CustomerID,Year进行分组,并对其Quantity实现聚合,相当于先做如下处理:
    */  			   
    SELECT CustomerID
           ,[Year]
           ,SUM(Quantity) AS Total
    FROM dbo.Orders
    GROUP BY CustomerID
           ,[Year]
    ORDER BY CustomerID			   
    
    /*	Result:		   
    CustomerID Year        Total
    ---------- ----------- -----------
    ANTON      1996        24
    ANTON      1997        295
    ANTON      1998        40
    BONAP      1996        181
    BONAP      1997        486
    BONAP      1998        313
    BOTTM      1996        81
    BOTTM      1997        454
    BOTTM      1998        421
    */
    /*
    2->pivot根据FOR [Year] IN子句中的值,在结果集中来建立对应的新列,本例中即是列,,
       对于新列,,中的取值,取中间结果集中与之相对应的值。
       如对于客户ANTON,1996列中的值就选择中间结果中对应的Total值,同理列中为。
       并将中间结果pivot表命名为x。
       
    3->最外层的SELECT语句从pivot表生成最终结果,此处因Orders表仅有列,故直接将结果用一个SELECT返回,有嵌套的SELECT参照下例。
      
    --结果:  
    CustomerID 1996        1997        1998
    ---------- ----------- ----------- -----------
    ANTON      24          295         40
    BONAP      181         486         313
    BOTTM      81          454         421
    */         以下是为输入表多于一列的例子,数据来源于SQL server 2005的AdventureWorks,其实现的原理同上。
    SELECT *  
    FROM(
    	SELECT YEAR(DueDate) [Year]
    		   ,CASE MONTH(DueDate)
    			WHEN 1 THEN 'January' 
    			WHEN 2 THEN 'February'
    			WHEN 3 THEN 'March'
    			WHEN 4 THEN 'April'
    			WHEN 5 THEN 'May'
    			WHEN 6 THEN 'June'
    			WHEN 7 THEN 'July'
    			WHEN 8 THEN 'August'
    			WHEN 9 THEN 'September'
    			WHEN 10 THEN 'October'
    			WHEN 11 THEN 'November'
    			WHEN 12 THEN 'December'
    		   END as [Month]
    		   ,ProductID
    		   ,OrderQty
    	FROM Production.WorkOrder
    )WorkOrder
        PIVOT (
               SUM(OrderQty)
               FOR [Month] IN ([January],[February],[March],[April],[May],[June],[July],[August],[September],[October],[November],[December])
              )x
    ORDER BY [Year], ProductID  
    --Result: 末尾部分省略
    /*  
    Year        ProductID   January     February    March       April       May         June        July        August      
    ----------- ----------- ----------- ----------- ----------- ----------- ----------- ----------- ----------- ----------- 
    2002        3           8480        16870       12960       9530        19390       14170       26200       35870             
    2002        316         1842        3704        2910        2252        4738        3496        7624        10778          
    2002        324         1842        3704        2910        2252        4738        3496        7546        10600            
    2002        327         921         1852        1455        1126        2369        1748        3773        5300              
    2002        328         414         1048        872         458         1272        992         1786        2632         
    */
    

    2. 动态PIVOT的使用

    1. USE AdventureWorks; 
    2. GO  
    3.  
    4. --第一种生成透视列的方法,使用了COALESCE来联接字符串  
    5. DECLARE @PivotColHeader VARCHAR(MAX)     
    6. SELECT @PivotColHeader = 
    7.     COALESCE(@PivotColHeader + ',[' + cast(Nameasvarchar) + ']'
    8.     '[' + cast(Nameasvarchar) + ']')   --示例中Name转换为varchar或char类型,注意:在CAST 和CONVERT 中使用varchar 时,显示n的默认值为30  
    9. FROM Sales.SalesTerritory 
    10. GROUPBYName 
    11.  
    12. /* 
    13. --第二种生成透视列的方法,使用了FOR XML PATH方法  
    14. SELECT @PivotColHeader =  
    15.     STUFF( 
    16.     (      
    17.           SELECTDISTINCT',[' + cast(Nameasvarchar) + ']' 
    18.           FROM Sales.SalesTerritory 
    19.           FOR XML PATH(''
    20.     ), 
    21.     1,1,''
    22. */ 
    23.  
    24. DECLARE @PivotTableSQL NVARCHAR(MAX
    25. SET @PivotTableSQL = N' 
    26.     SELECT
    27.     FROM
    28.         SELECTYEAR(H.OrderDate) [Year
    29.             ,T.Name 
    30.             ,H.TotalDue 
    31.         FROM Sales.SalesOrderHeader H 
    32.             LEFTJOIN Sales.SalesTerritory T 
    33.                 ON H.TerritoryID = T.TerritoryID 
    34.     )AS PivotData 
    35.         PIVOT( 
    36.             SUM(TotalDue) 
    37.             FORNameIN
    38.                          ' + @PivotColHeader + ' 
    39.                          ) 
    40.                ) AS x '                             
    41. EXECUTE sp_executesql @PivotTableSQL     
    42.  
    43. --Result:部分结果省略  
    44. /* 
    45. Year        Australia             Canada                Central               France                Germany               Northeast              
    46. ----------- --------------------- --------------------- --------------------- --------------------- --------------------- ---------------------   
    47. 2001        1446497.1744          2173647.1453          1263884.1024          199531.723            262752.4184           754833.2045            
    48. 2002        2380484.8387          7215430.5017          3518185.4756          1717145.7439          575960.0974           3275322.1694           
    49. 2003        4547123.2777          8186021.9178          4015356.874           4366078.3475          2714826.4297          3833030.25            
    50. 2004        3823410.2386          3926712.8926          1771532.7396          2853948.6596          2386224.5508          1406555.6861          
    51.  
    52. */    
    USE AdventureWorks;
    GO 
    
    --第一种生成透视列的方法,使用了COALESCE来联接字符串
    DECLARE @PivotColHeader VARCHAR(MAX)    
    SELECT @PivotColHeader =
        COALESCE(@PivotColHeader + ',[' + cast(Name as varchar) + ']',
        '[' + cast(Name as varchar) + ']')   --示例中Name转换为varchar或char类型,注意:在CAST 和CONVERT 中使用varchar 时,显示n的默认值为30
    FROM Sales.SalesTerritory
    GROUP BY Name
    
    /*
    --第二种生成透视列的方法,使用了FOR XML PATH方法
    SELECT @PivotColHeader = 
        STUFF(
        (     
              SELECT DISTINCT ',[' + cast(Name as varchar) + ']'
              FROM Sales.SalesTerritory
              FOR XML PATH('')
        ),
        1,1,'')
    */
    
    DECLARE @PivotTableSQL NVARCHAR(MAX)
    SET @PivotTableSQL = N'
        SELECT *
        FROM (
            SELECT YEAR(H.OrderDate) [Year]
    			,T.Name
    			,H.TotalDue
            FROM Sales.SalesOrderHeader H
    			LEFT JOIN Sales.SalesTerritory T
    			    ON H.TerritoryID = T.TerritoryID
        )AS PivotData
            PIVOT(
                SUM(TotalDue)
                FOR Name IN (
                             ' + @PivotColHeader + '
                             )
                   ) AS x '                            
    EXECUTE sp_executesql @PivotTableSQL    
    
    --Result:部分结果省略
    /*
    Year        Australia             Canada                Central               France                Germany               Northeast             
    ----------- --------------------- --------------------- --------------------- --------------------- --------------------- --------------------- 
    2001        1446497.1744          2173647.1453          1263884.1024          199531.723            262752.4184           754833.2045           
    2002        2380484.8387          7215430.5017          3518185.4756          1717145.7439          575960.0974           3275322.1694          
    2003        4547123.2777          8186021.9178          4015356.874           4366078.3475          2714826.4297          3833030.25           
    2004        3823410.2386          3926712.8926          1771532.7396          2853948.6596          2386224.5508          1406555.6861         
    
    */   
    

       对该动态pivot增加汇总列

    1. DECLARE @PivotColHeader VARCHAR(MAX
    2. DECLARE @TotalCol VARCHAR(MAX
    3.  
    4. SELECT @PivotColHeader =                              --使用COALESCE函数生成列标题  
    5.     COALESCE(@PivotColHeader + ',[' + cast(Nameasvarchar) + ']'
    6.     '[' + cast(Nameasvarchar) + ']'
    7.     , 
    8.     @TotalCol = COALESCE(@TotalCol + ', SUM([' + cast(Nameasvarchar) + ']) AS [' + cast(Nameasvarchar) + ']' 
    9.      ,'SUM([' + cast(Nameasvarchar) + ']) AS [' + cast(Nameasvarchar) + ']')     --使用COALESCE函数生成汇总字符串  
    10. FROM Sales.SalesTerritory 
    11.  
    12. DECLARE @PivotTableSQL NVARCHAR(MAX
    13. SET @PivotTableSQL = N' 
    14.     SELECT
    15.     FROM
    16.         SELECTCAST(YEAR(H.OrderDate) ASCHAR(4)) [Year
    17.         ,T.Name 
    18.         ,H.TotalDue 
    19.         FROM Sales.SalesOrderHeader H 
    20.         LEFTJOIN Sales.SalesTerritory T 
    21.            ON H.TerritoryID = T.TerritoryID 
    22.     )AS PivotData 
    23.         PIVOT( 
    24.             SUM(TotalDue) 
    25.             FORNameIN
    26.              ' + @PivotColHeader + ' 
    27.                    ) 
    28.             ) AS x    
    29.     UNION  
    30.     SELECT''GrandTotal'', ' + @TotalCol + ' 
    31.     FROM
    32.     SELECTCAST(YEAR(H.OrderDate) ASCHAR(4)) [Year
    33.         ,T.Name 
    34.         ,H.TotalDue 
    35.     FROM Sales.SalesOrderHeader H 
    36.         LEFTJOIN Sales.SalesTerritory T 
    37.         ON H.TerritoryID = T.TerritoryID 
    38.     ) AS PivotData                                                   
    39.         PIVOT( 
    40.             SUM(TotalDue) 
    41.             FORNameIN
    42.                 ' + @PivotColHeader + ' 
    43.                    ) 
    44.              ) AS y '    
    45. --PRINT  @PivotTableSQL                                          
    46. EXECUTE sp_executesql @PivotTableSQL  
    47.  
    48. --Result:部分结果省略  
    49. /* 
    50. Year       Australia             Canada                Central               France                Germany               Northeast             Northwest              
    51. ---------- --------------------- --------------------- --------------------- --------------------- --------------------- --------------------- ---------------------   
    52. 2001       1446497.1744          2173647.1453          1263884.1024          199531.723            262752.4184           754833.2045           2703481.7947           
    53. 2002       2380484.8387          7215430.5017          3518185.4756          1717145.7439          575960.0974           3275322.1694          5651688.6685           
    54. 2003       4547123.2777          8186021.9178          4015356.874           4366078.3475          2714826.4297          3833030.25            7494658.0357           
    55. 2004       3823410.2386          3926712.8926          1771532.7396          2853948.6596          2386224.5508          1406555.6861          4952772.2793           
    56. GrandTotal 12197515.5294         21501812.4574         10568959.1916         9136704.474           5939763.4963          9269741.31            20802600.7782          
    57. */     
    DECLARE @PivotColHeader VARCHAR(MAX)
    DECLARE @TotalCol VARCHAR(MAX)
    
    SELECT @PivotColHeader =                              --使用COALESCE函数生成列标题
        COALESCE(@PivotColHeader + ',[' + cast(Name as varchar) + ']',
        '[' + cast(Name as varchar) + ']')
        ,
        @TotalCol = COALESCE(@TotalCol + ', SUM([' + cast(Name as varchar) + ']) AS [' + cast(Name as varchar) + ']'
         ,'SUM([' + cast(Name as varchar) + ']) AS [' + cast(Name as varchar) + ']')     --使用COALESCE函数生成汇总字符串
    FROM Sales.SalesTerritory
    
    DECLARE @PivotTableSQL NVARCHAR(MAX)
    SET @PivotTableSQL = N'
        SELECT *
        FROM (
            SELECT CAST(YEAR(H.OrderDate) AS CHAR(4)) [Year]
    		,T.Name
    		,H.TotalDue
            FROM Sales.SalesOrderHeader H
    		LEFT JOIN Sales.SalesTerritory T
    		   ON H.TerritoryID = T.TerritoryID
        )AS PivotData
    		PIVOT(
    			SUM(TotalDue)
    			FOR Name IN (
    			 ' + @PivotColHeader + '
    				   )
    		    ) AS x   
        UNION 
        SELECT ''GrandTotal'', ' + @TotalCol + '
        FROM (
    	SELECT CAST(YEAR(H.OrderDate) AS CHAR(4)) [Year]
    		,T.Name
    		,H.TotalDue
    	FROM Sales.SalesOrderHeader H
    	    LEFT JOIN Sales.SalesTerritory T
    		ON H.TerritoryID = T.TerritoryID
    	) AS PivotData													
    		PIVOT(
    			SUM(TotalDue)
    			FOR Name IN (
    				' + @PivotColHeader + '
    				   )
    		     ) AS y '   
    --PRINT  @PivotTableSQL                                        
    EXECUTE sp_executesql @PivotTableSQL 
    
    --Result:部分结果省略
    /*
    Year       Australia             Canada                Central               France                Germany               Northeast             Northwest             
    ---------- --------------------- --------------------- --------------------- --------------------- --------------------- --------------------- --------------------- 
    2001       1446497.1744          2173647.1453          1263884.1024          199531.723            262752.4184           754833.2045           2703481.7947          
    2002       2380484.8387          7215430.5017          3518185.4756          1717145.7439          575960.0974           3275322.1694          5651688.6685          
    2003       4547123.2777          8186021.9178          4015356.874           4366078.3475          2714826.4297          3833030.25            7494658.0357          
    2004       3823410.2386          3926712.8926          1771532.7396          2853948.6596          2386224.5508          1406555.6861          4952772.2793          
    GrandTotal 12197515.5294         21501812.4574         10568959.1916         9136704.474           5939763.4963          9269741.31            20802600.7782         
    */    
    

    生成汇总列的注意事项;     1->使用COALESCE函数生成列标题 。     2->使用COALESCE函数生成带有SUM求和函数并且指定了别名的字符串。     3->使用UNION对两个SELECT来实现联接。且将[Year]转换为字符串,因为YEAR(H.OrderDate)得值为 INT ,而''GrandTotal''为字符串,UNION 或UNION ALL使用时必须列的数量和类型相对应。

  • 相关阅读:
    Kotlin调用lambda表达式时,lambda中的参数名字省略问题
    Kotlin读取控制台输入
    安卓P(9)及以上Cleartext HTTP traffic to xxx not permitted错误,无法HTTP明文连接错误解决方法
    AS4.0以上查看R.id
    Kotlin Standard.kt解析(also,apply,let.run...)
    ext4文件解包打包
    Kotlin中的var、val和const
    Kotlin的构造方法探究
    Markdown语法
    uniapp遇到的小问题
  • 原文地址:https://www.cnblogs.com/freeliver54/p/3117179.html
Copyright © 2011-2022 走看看