好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

sqlserver技术内幕&lt;二&gt;表运算符之pivot

例一: 在Sql Server的帮助文档中,对Pivot函数是这样解释的: 可以使用 PIVOT 和 UNPIVOT 关系运算符对表表达式进行操作以获得另一个表。PIVOT 通过将表达式某一列中的唯一转换为 输出中的多个列来转换表表达式,并在必要时对最终 输出中所需的任何其余的列执

例一:

在Sql Server的帮助文档中,对Pivot函数是这样解释的:
可以使用 PIVOT 和 UNPIVOT 关系运算符对表值表达式进行操作以获得另一个表。PIVOT 通过将表达式某一列中的唯一值转换为 输出中的多个列来转换表值表达式,并在必要时对最终 输出中所需的任何其余的列值执行聚合.

对第一次使用PIVOT函数的朋友来说,这样的解释很难让大家理解,下面编辑用PIVOT函数来实现一个行转列的功能,以便让读者更容易理解该函数.

注意:PIVOT是Sql Server2005的新函数,2005前行转列请参看本站:
SQLServer中(行列转换)行转列及列转行且加平均值及汇总值

先创建一个工资表:

Create Table Salary
(
HrName varchar(50),
Monthly varchar(50),
Money money
)

往表中插入数据:

insert into Salary(HrName,Monthly,[Money])
select '张三','一月','3000'
union all
select '张三','二月','3200'
union all
select '张三','三月','3500'
union all
select '李四','一月','3800'
union all
select '李四','二月','4200'
union all
select '李四','三月','3900'
union all
select '张三','一月','2000'

查看正常的数据:

select * from Salary

结果:

HrName Monthly Money
张三 一月 3000.00
张三 二月 3200.00
张三 三月 3500.00
李四 一月 3800.00
李四 二月 4200.00
李四 三月 3900.00
张三 一月 2000.00


查看行转列后的数据:

select HrName as '姓名',[一月],[二月],[三月] from Salary
pivot(sum([Money]) for Monthly in ([一月],[二月],[三月])) as pvt

结果:

姓名 一月 二月 三月
李四 3800.00 4200.00 3900.00
张三 5000.00 3200.00 500.00


注意:
pivot(sum([Money]) for Monthly in ([一月],[二月],[三月])) 中的sum([Money]),这里必须是聚合函数,比如是min,max等。
in ([一月],[二月],[三月])中的[一月],[二月],[三月]即为Monthly的Value,又为新结果集的列名.

如果我们将其中的一月改为四月,因为数据源中没有四月的记录,所以四月查询出来应该为Null.
测试:

select HrName as '姓名',[四月],[二月],[三月] from Salary
pivot(sum([Money]) for Monthly in ([四月],[二月],[三月])) as pvt

结果:

姓名 四月 二月 三月
李四 NULL 4200.00 3900.00
张三 NULL 3200.00 3500.00


例二:

在SQLServer 2000环境中,如果要实现交叉表格报表,主要是靠一系列复杂的 SELECT...CASE 语句.

其实现过程请参阅这里T-SQL 交叉报表(行列互换) 交叉查询 旋转查询

在SQLServer 2005中我们可以使用PIVOT关系运算符来实现行列转换.

还是以学生成绩表来举例:

id姓名 科目 成绩

1 张三 语文 60
2 张三 数学 65
3 张三 外语 70
4 李四 语文 80
5 李四 数学 90
6 李四 外语 85
7 王五 语文 70
8 王五 数学 71
9 王五 外语 75
10 赵六 语文 64
11 赵六 数学 67
12 赵六 外语 76

查询后得出:

姓名 语文数学外语

李四 80 90 85
王五 70 71 75
张三 60 65 70
赵六 64 67 76

-- 准备数据 :

select * from sysobjects where [xtype] = 'u'

go

if exists( select id from sysobjects where name = 'studentscore' )

drop table studentscore -- 删除与实验冲突的表

go

create table studentscore -- 创建实验表

(

[id] int identity ( 1 , 1 ),

[name] nvarchar ( 20 ) not null,

subject nvarchar ( 20 ) not null,

score int not null

)

go

select * from studentscore

go

-- 添加实验数据

insert studentscore values ( ' 张三 ' , ' 语文 ' , '60' );

insert studentscore values ( ' 张三 ' , ' 数学 ' , '65' );

insert studentscore values ( ' 张三 ' , ' 外语 ' , '70' );

insert studentscore values ( ' 李四 ' , ' 语文 ' , '80' );

insert studentscore values ( ' 李四 ' , ' 数学 ' , '90' );

insert studentscore values ( ' 李四 ' , ' 外语 ' , '85' );

insert studentscore values ( ' 王五 ' , ' 语文 ' , '70' );

insert studentscore values ( ' 王五 ' , ' 数学 ' , '71' );

insert studentscore values ( ' 王五 ' , ' 外语 ' , '75' );

insert studentscore values ( ' 赵六 ' , ' 语文 ' , '64' );

insert studentscore values ( ' 赵六 ' , ' 数学 ' , '67' );

insert studentscore values ( ' 赵六 ' , ' 外语 ' , '76' );

go

select * from studentscore

go

使用 SELECT...CASE 语句实现代码如下

select [name] ,

语文 = max ( case

when subject = ' 语文 ' then score else 0

end ),

数学 = max ( case

when subject = ' 数学 ' then score else 0

end ),

外语 = max ( case

when subject = ' 外语 ' then score else 0

end )

from studentscore

group by [name]

结果:

下面我们使用PIVOT关系运算符来实现行列转换

select [name] , [ 语文 ] as ' 语文 ' , [ 数学 ] as ' 数学 ' , [ 外语 ] as ' 外语 '

from ( select score , subject , [name] from studentscore ) as ss

pivot

(

sum ( score ) for subject in( [ 语文 ] , [ 数学 ] , [ 外语 ] )

) as pvt

结果:用较少的代码完成了交叉表格报表

============================

对于这种方法要注意的一点是,我们使用sum()聚合函数,表面上没有指定按什么方式分组,但是自动按照name列分组了.

怎么做到的呢?原来 pivot关系运算符会根据前面的对象中的列来自行判断,在这个例子中 pivot前面的对象是ss,是个子查询,这个子查询中只有三列,score , subject 和 [name],但是 pivot运算符内部使用了score 和 subject这两列,那么肯定是对[name]分组.

所以我们得出, pivot运算符的分组规则是,跟随对象中的那些不在 pivot运算符内部的列:

为了好理解我们再写一个例子:

-- 在 ss 这个子查询中 , 多加一列 id

-- 那么 pivot 应该按照 name 和 id 进行分组

select [name] , [ 语文 ] as ' 语文 ' , [ 数学 ] as ' 数学 ' , [ 外语 ] as ' 外语 '

from ( select score , subject , [name],id from studentscore ) as ss

pivot

(

sum ( score ) for subject in( [ 语文 ] , [ 数学 ] , [ 外语 ] )

) as pvt

结果:验证了我们的设想

UNPIVOT关系运算符从字面上来看,就知道它的用途正好和PIVOT相反,下面举例说明:

if exists( select id from sysobjects where name = 'studentscore' )

drop table studentscore -- 删除与实验冲突的表

go

create table studentscore -- 创建实验表

(

[id] int identity ( 1 , 1 ),

[name] nvarchar ( 20 ) not null,

yuwen int not null,

shuxue int not null,

waiyu int not null

)

go

select * from studentscore

go

-- 添加实验数据

insert studentscore values ( ' 张三 ' , '60' , '65' , '70' );

insert studentscore values ( ' 李四 ' , '80' , '90' , '86' );

insert studentscore values ( ' 王五 ' , '70' , '71' , '75' );

insert studentscore values ( ' 赵六 ' , '64' , '67' , '76' );

go

select * from studentscore

go

结果:

SELECT id , [name] , subject , score

FROM

( SELECT id , [name] , 语文 = yuwen , 数学 = shuxue , 外语 = waiyu

FROM studentscore ) as ss

UNPIVOT

( score FOR subject IN

( 语文 , 数学 , 外语 )

) AS unpvt

结果:

查看更多关于sqlserver技术内幕&lt;二&gt;表运算符之pivot的详细内容...

  阅读:44次