使用 PIVOT 和 UNPIVOT

系统 1766 0

http://msdn.microsoft.com/zh-cn/library/ms177410%28v=SQL.90%29.aspx

可以使用 PIVOT 和 UNPIVOT 关系运算符将表值表达式更改为另一个表。PIVOT 通过将表达式某一列中的唯一值转换为输出中的多个列来旋转表值表达式,并在必要时对最终输出中所需的任何其余列值执行聚合。UNPIVOT 与 PIVOT 执行相反的操作,将表值表达式的列转换为列值。

ms177410.note(zh-cn,SQL.90).gif 注意:

对升级到 Microsoft SQL Server 2005 的数据库使用 PIVOT 和 UNPIVOT 时,数据库的兼容级别必须设置为 90。有关如何设置数据库兼容级别的信息,请参阅 sp_dbcmptlevel (Transact-SQL)

PIVOT 提供的语法比一系列复杂的 SELECT...CASE 语句中所指定的语法更简单和更具可读性。有关 PIVOT 语法的完整说明,请参阅 FROM (Transact-SQL)

以下是带批注的 PIVOT 语法。

SELECT < non-pivoted column > ,

    [ first pivoted column ] AS < column name > ,

    [ second pivoted column ] AS < column name > ,

...

    [ last pivoted column ] AS < column name >

FROM

( < SELECT query that produces the data > )

AS < alias for the source query >

PIVOT

(

<aggregation function>( < column being aggregated > )

FOR

[< column that contains the values that will become column headers >]

IN ( [ first pivoted column ] , [ second pivoted column ] ,

... [ last pivoted column ] )

) AS < alias for the pivot table >

< optional ORDER BY clause >

SELECT <non-pivoted column>,

    [first pivoted column] AS <column name>,

    [second pivoted column] AS <column name>,

    ...

    [last pivoted column] AS <column name>

FROM

    (<SELECT query that produces the data>)

   AS <alias for the source query>

PIVOT

(

    <aggregation function>(<column being aggregated>)

FOR

[<column that contains the values that will become column headers>]

    IN ( [first pivoted column], [second pivoted column],

    ... [last pivoted column])

) AS <alias for the pivot table>

<optional ORDER BY clause>

简单 PIVOT 示例

下面的代码示例生成一个两列四行的表。

    USE AdventureWorks ;

GO

SELECT DaysToManufacture, AVG(StandardCost) AS AverageCost 

FROM Production.Product

GROUP BY DaysToManufacture




  

下面是结果集:

DaysToManufacture          AverageCost

0                          5.0885

1                          223.88

2                          359.1082

4                          949.4105

没有定义 DaysToManufacture 为 3 的产品。

以下代码显示相同的结果,该结果经过透视以使 DaysToManufacture 值成为列标题。提供一个列表示三 [3] 天,即使结果为 NULL

    -- Pivot table with one row and five columns

SELECT 'AverageCost' AS Cost_Sorted_By_Production_Days, 

[0], [1], [2], [3], [4]

FROM

(SELECT DaysToManufacture, StandardCost     FROM Production.Product) AS SourceTable

PIVOT

(

AVG(StandardCost)

FOR DaysToManufacture IN ([0], [1], [2], [3], [4])

) AS PivotTable




  

下面是结果集:

Cost_Sorted_By_Production_Days    0         1         2           3       4      

AverageCost                       5.0885    223.88    359.1082    NULL    949.4105

复杂 PIVOT 示例

可能会用到 PIVOT 的常见情况是:需要生成交叉表格报表以汇总数据。例如,假设需要在 AdventureWorks 示例数据库中查询 PurchaseOrderHeader 表以确定由某些特定雇员所下的采购订单数。以下查询提供了此报表(按供应商排序)。

    USE AdventureWorks;

GO

SELECT VendorID, [164] AS Emp1, [198] AS Emp2, [223] AS Emp3, [231] AS Emp4, [233] AS Emp5

FROM 

(SELECT PurchaseOrderID, EmployeeID, VendorID

FROM Purchasing.PurchaseOrderHeader) p

PIVOT

(

COUNT (PurchaseOrderID)

FOR EmployeeID IN

( [164], [198], [223], [231], [233] )

) AS pvt

ORDER BY VendorID


  

以下为部分结果集。

    VendorID    Emp1        Emp2        Emp3        Emp4        Emp5

1           4           3           5           4           4

2           4           1           5           5           5

3           4           3           5           4           4

4           4           2           5           5           4

5           5           1           5           5           5


  

将在 EmployeeID 列上透视此嵌套 select 语句返回的结果。

    SELECT PurchaseOrderID, EmployeeID, VendorID

FROM PurchaseOrderHeader


  

这意味着 EmployeeID 列返回的唯一值自行变成了最终结果集中的字段。结果,在透视子句中指定的每个 EmployeeID 号都有相应的一列:在本例中为雇员 164 198 223 231 233 PurchaseOrderID 列作为值列,将根据此列对最终输出中返回的列(称为分组列)进行分组。在本例中,通过 COUNT 函数聚合分组列。请注意,将显示一条警告消息,指出为每个雇员计算 COUNT 时未考虑显示在 PurchaseOrderID 列中的任何空值。

ms177410.note(zh-cn,SQL.90).gif 重要提示:

如果聚合函数与 PIVOT 一起使用,则计算聚合时将不考虑出现在值列中的任何空值。

UNPIVOT 将与 PIVOT 执行几乎完全相反的操作,将列转换为行。假设以上示例中生成的表在数据库中存储为 pvt ,并且您需要将列标识符 Emp1 Emp2 Emp3 Emp4 Emp5 旋转为对应于特定供应商的行值。这意味着必须标识另外两个列。包含要旋转的列值( Emp1 Emp2 ...)的列将被称为 Employee ,将保存当前位于待旋转列下的值的列被称为 Orders 。这些列分别对应于 Transact-SQL 定义中的 pivot_column value_column 。以下为该查询。

    --Create the table and insert values as portrayed in the previous example.

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


  

以下为部分结果集。

    VendorID   Employee   Orders

1      Emp1         4

1      Emp2         3

1      Emp3         5

1      Emp4         4

1      Emp5         4

2      Emp1         4

2      Emp2         1

2      Emp3         5

2      Emp4         5

2      Emp5         5

...


  

请注意,UNPIVOT 并不完全是 PIVOT 的逆操作。PIVOT 会执行一次聚合,从而将多个可能的行合并为输出中的单个行。而 UNPIVOT 不会重现原始表值表达式的结果,因为行已经被合并了。另外,UNPIVOT 的输入中的空值不会显示在输出中,而在执行 PIVOT 操作之前,输入中可能有原始的空值。

AdventureWorks 示例数据库中的 Sales.vSalesPersonSalesByFiscalYears 视图将使用 PIVOT 返回每个销售人员在每个会计年度的总销售额。若要在 SQL Server Management Studio 中编写视图脚本,请在 “对象资源管理器” 中,在 “视图” 文件夹下找到 AdventureWorks 数据库对应的视图。右键单击该视图名称,再选择 “编写视图脚本为”

使用 PIVOT 和 UNPIVOT


更多文章、技术交流、商务合作、联系博主

微信扫码或搜索:z360901061

微信扫一扫加我为好友

QQ号联系: 360901061

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描下面二维码支持博主2元、5元、10元、20元等您想捐的金额吧,狠狠点击下面给点支持吧,站长非常感激您!手机微信长按不能支付解决办法:请将微信支付二维码保存到相册,切换到微信,然后点击微信右上角扫一扫功能,选择支付二维码完成支付。

【本文对您有帮助就好】

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描上面二维码支持博主2元、5元、10元、自定义金额等您想捐的金额吧,站长会非常 感谢您的哦!!!

发表我的评论
最新评论 总共0条评论