所谓表旋转,就是将表的行转换为列,或是将表的列转换为行,这是从SQL Server 2005开始提供的新技术。因此,如果希望使用此功能,需要将数据库的兼容级别设置为90。表旋转在某些方面也是解决了表的数据存储和实际需要之间的矛盾。例如,图9-4所示的是一个典型的产品销售统计表,这种格式虽然便于阅读,但是在进行数据表存储的时候却并不容易管理,产品销售数据表通常需要设计成图9-5所示的结构。这样就带来一个问题,用户既希望数据容易管理,又希望能够生成一种能够容易阅读的表格数据,这时候就可以使用表旋转技术。

图9-4 产品销售表

图9-5 数据表结构
9.4.1 PIVOT运算符
PIVOT运算符用于将表的行转换为列,并能同时对行执行聚合运算。其语法格式如下:
SELECT <非旋转列>,
[第一个旋转列] AS <列名>,
[第二个旋转列] AS <列名>, < http://www.2cto.com/kf/ware/vc/" target="_blank" class="keylink">vcD48cD4gICAgLi4uIDwvcD48cD4gICAgW9fuuvO1xND916rB0F0gQVMgJmx0O8HQw/smZ3Q7IDwvcD48cD5GUk9NIDwvcD48cD4gICAgKCZsdDtTRUxFQ1Qgyfqzycr9vt21xLLp0a8mZ3Q7KSBBUyAmbHQ7zqrUtLLp0a+94bn71ri2qLXEsfDD+yZndDs8L3A+PHA+IDwvcD48cD5QSVZPVCA8L3A+PHA+KCA8L3A+PHA+ICAgICZsdDu+27rPuq/K/SZndDsoJmx0O7G7vtu6z7XEwdAmZ3Q7KSA8L3A+PHA+Rk9SIDwvcD48cD5bJmx0O7D8uqy9q7G716q7u86qwdCx6s23tcTWtbXEwdAmZ3Q7XSA8L3A+PHA+ICAgIElOICggW7Xa0ru49tD916q687XEwdBdLCBbtdq2/rj20P3XqrrztcTB0F0sIDwvcD48cD4gICAgLi4uIFvX7rrz0ru49tD916q687XEwdBdKSA8L3A+PHA+KSBBUyAmbHQ7zqrQ/deqse3WuLaotcSx8MP7Jmd0OyA8L3A+PHA+IDwvcD48cD4mbHQ7v8nRobXEIE9SREVSIEJZINfTvuQmZ3Q7OzwvcD48cD7OqsHLyrXP1tDQtcTQ/deqo6zUtLLp0a+78bXDtcS94bn706a1sb7fsbjI/cHQo6yyxcTcubvKtc/W0P3XqqGjtdoxwdDKx7K7vfjQ0ND916q1xMHQo6zK9NPaserWvsHQo7u12jLB0MrHyvTQ1MHQo6zSsrPGzqrNuMrTwdCjrMbk1tC1xNa1u+Gxu9D916rB0MP7o7u12jPB0MrHyvTQ1Na1wdCjrNXi0KnWtb2r1/fOqtDCwdC1xNa1oaPKudPDz8LD5rXE0+++5LS0vajSu7j2yr7A/bHtT3JkZXJzo6zE2sjdyOex7TktN8v5yr6hozwvcD48cD5DUkVBVEUgVEFCTEUgT3JkZXJzPC9wPjxwPig8L3A+PHA+ICAgUHJvZHVjdElEIGludCBOT1QgTlVMTCw8L3A+PHA+ICAgT3JkZXJEYXRlIGRhdGV0aW1lIE5PVCBOVUxMLDwvcD48cD4gICBTaGlwVG8gY2hhcigyMCkgTk9UIE5VTEwsPC9wPjxwPiAgIFN1YlRvdGFsIG1vbmV5IE5PVCBOVUxMPC9wPjxwPik7PC9wPjxwPklOU0VSVCBJTlRPIE9yZGVycyA8L3A+PHA+VkFMVUVTICgxLENBU1Qo"20090102' AS datetime), 'Shanghai', 100.00),
(1, CAST('20090105' AS datetime), 'Shanghai',100.00),
(1, CAST('20090123' AS datetime),'Jinan', 100.00),
(2, CAST('20090125' AS datetime),'Shanghai', 100.00),
(1, CAST('20090205' AS datetime),'Jinan', 100.00),
(3, CAST('20090213' AS datetime),'Shanghai', 100.00),
(3, CAST('20090219' AS datetime),'Shanghai', 100.00),
(4, CAST('20090309' AS datetime),'Beijing', 100.00),
(1, CAST('20090311' AS datetime),'Dalian', 100.00),
(2, CAST('20090324' AS datetime),'Shanghai', 100.00),
(3, CAST('20090326' AS datetime),'Wuhan', 100.00);
表9-7 Orders表的内容
ProductID |
OrderDate |
ShipTo |
SubTotal |
1 |
2009-01-02 00:00:00.000 |
Shanghai |
100.00 |
1 |
2009-01-05 00:00:00.000 |
Shanghai |
100.00 |
1 |
2009-01-23 00:00:00.000 |
Jinan |
100.00 |
2 |
2009-01-25 00:00:00.000 |
Shanghai |
100.00 |
1 |
2009-02-05 00:00:00.000 |
Jinan |
100.00 |
3 |
2009-02-13 00:00:00.000 |
Shanghai |
100.00 |
3 |
2009-02-19 00:00:00.000 |
Shanghai |
100.00 |
4 |
2009-03-09 00:00:00.000 |
Beijing |
100.00 |
1 |
2009-03-11 00:00:00.000 |
Dalian |
100.00 |
2 |
2009-03-24 00:00:00.000 |
Shanghai |
100.00 |
3 |
2009-03-26 00:00:00.000 |
Wuhan |
100.00 |
Orders表中包含了3个月的产品销售数据,现在假设要获得像图9-4所示的销售表,则对源表的查询首先需要获得上面讲的三列,参考下面的语句:
SELECT ProductID,
MONTH(OrderDate) AS OrderMonth,
SubTotal
FROM Orders;
查询结果如表9-8所示。其中ProductID为标志列,OrderMonth为属性列,其中的月份要转变为列的名称,SubTotal为属性值列,这些值将成为新列的值。
表9-8 获取到的三列内容
ProductID |
OrderMonth |
SubTotal |
1 |
1 |
100.00 |
1 |
1 |
100.00 |
1 |
1 |
100.00 |
2 |
1 |
100.00 |
1 |
2 |
100.00 |
3 |
2 |
100.00 |
3 |
2 |
100.00 |
4 |
3 |
100.00 |
1 |
3 |
100.00 |
2 |
3 |
100.00 |
3 |
3 |
100.00 |
完整的旋转查询语句如下。查询结果如表9-9所示。
SELECT ProductID,
[1]AS Jan,
[2]AS Feb,
[3]AS Mar
FROM (SELECT ProductID, MONTH(OrderDate) ASOrderMonth, SubTotal
FROMOrders) AS O1
PIVOT
(
SUM(SubTotal)
FOROrderMonth IN ([1], [2], [3])
) AS Pvt
ORDER BY ProductID;
表9-9 旋转后输出的内容
ProductID |
Jan |
Feb |
Mar |
1 |
300.00 |
100.00 |
100.00 |
2 |
100.00 |
NULL |
100.00 |
3 |
NULL |
200.00 |
100.00 |
4 |
NULL |
NULL |
100.00 |
上面的查询语句将按下面的步骤来获取表9-9所示的结果集:
PIVOT首先按属性值列之外的列(ProductID和OrderMonth)对输入表Sales.Orders进行分组汇总,类似执行下面的语句,得到一个如表9-10所示的中间结果集。
SELECT ProductID,
OrderMonth,
SUM(SubTotal) AS SubTotal
FROM (SELECT ProductID,MONTH(OrderDate) AS OrderMonth, SubTotal
FROM Orders) AS O1
GROUP BY ProductID,OrderMonth;
表9-10 Orders经分组汇总后的结果
ProductID |
OrderMonth |
SubTotal |
1 |
1 |
300.00 |
1 |
2 |
100.00 |
1 |
3 |
100.00 |
2 |
1 |
100.00 |
2 |
3 |
100.00 |
3 |
2 |
200.00 |
3 |
3 |
100.00 |
4 |
3 |
100.00 |
PIVOT根据FOR OrderMonth IN指定的值1、2、3,首先在结果集中建立名为1、2、3的列,然后从表9-10所示的中间结果中取出SubTotal列中取出相符合的值,分别放置到1、2、3列中。此时得到的结果集的别名为pvt(见语句中AS pvt的指定)。结果集的内容如表9-11所示。
表9-11 使用FOR OrderMonth IN ([1], [2], [3])后得到的结果集
ProductID |
1 |
2 |
3 |
1 |
300.00 |
100.00 |
100.00 |
2 |
100.00 |
NULL |
100.00 |
3 |
NULL |
200.00 |
100.00 |
4 |
NULL |
NULL |
100.00 |
最后根据SELECT ProductID, [1] AS Jan,[2] AS Feb, [3] AS Mar FROM的指定,从别名pvt结果集中检索数据,并分别将名为1、2、3的列在最终结果集中重新命名为Jan、Feb、Mar,得到表9-9所示的结果集。这里