(转至CSDN-爱新觉罗.毓华)
坚持SQL Server的学习, 列转行这个也是比较常见SQL操作
本人愚见, 总结出来具有以下条件的情况下可能会出现此需求: 一个表中某一个列值依赖于该表中两个或多个列(如下表中的成绩依赖于学生和课程), 好像这种情况违反了数据库设计的第一范式(1NF), 哈哈!
今天CSDN上无意看到一个比较全面的列转行, 但是以前没怎么接触心里就有种恐惧,现在系统的学习下,避免以后遇见相同的问题的又束手无策了!
还是开始创建测试环境
use
tempdb;
go
if
object_id
(
'
tb
'
)
is
not
null
drop
table
tb
go
create
table
tb(name
varchar
(
10
) , course
varchar
(
10
) , scores
int
)
insert
into
tb
values
(
'
张三
'
,
'
语文
'
,
74
)
insert
into
tb
values
(
'
张三
'
,
'
数学
'
,
83
)
insert
into
tb
values
(
'
张三
'
,
'
物理
'
,
93
)
insert
into
tb
values
(
'
李四
'
,
'
语文
'
,
74
)
insert
into
tb
values
(
'
李四
'
,
'
数学
'
,
84
)
insert
into
tb
values
(
'
李四
'
,
'
物理
'
,
94
)
go
select
*
from
tb
挺简单的一张表,描述了学生的课程成绩信息,其中有3个字段,依次是姓名,课程,分数
测试数据中有2个学生,分别是张三和李四,而课程有语文,数学,物理3课,查询结果集如下
现有这一需求, 将成绩转为行显示,学科为列, 结果如下
结果改变了什么, 行数改变了,跟学生数量有关,一个学生一行,这里就肯定是用学生姓名来分组了
除姓名外,增加了3列,跟课程数量有关,根据不同的课程创建新列, 这里就是用 "case when"了,代码如下:
--静态实现
select
name 姓名,
max
(
case
course
when
'
语文
'
then
scores
end
) 语文,
max
(
case
course
when
'
数学
'
then
scores
end
) 数学,
max
(
case
course
when
'
物理
'
then
scores
end
) 物理
from
tb
group
by
name
这里的then后的值都是成绩, 没有区别; 只是条件不同, 属于哪一科的成绩就放在那一列下, 因为是逐行扫描, 每次只有单个成绩, 所以这里的max函数只是为了骗过group分组的幌子, 没有任何意义, 执行查询结果如上所示
现将需求稍微小小改变一下, 课程为行学生为列成绩不变在中间, 其实这与上面的一个问题类似, 都是将成绩以行的形式显示, 只是"case when" 和 group不一样而已, 知道原理了, 稍微变通一下, 应该没问题, 哈哈!
select
course 课程,
max
(
case
name
when
'
张三
'
then
scores
end
) 张三,
max
(
case
name
when
'
李四
'
then
scores
end
) 李四
from
tb
group
by
course
--动态实现
以上虽然实现了功能, 但是比较死板, 它是静态的, 也就是课程只允许有语文, 数学, 物理, 若增加了其他课程就不行了, 这样我们得动态生成sql语句, 才能根据不同的课程生成不同的sql, 然后执行, 以前从未接触了动态生成sql, 呵呵! 这次看到了才发现了sql编程与开发语言编程有异曲同工之妙, 请看下面动态生成的SQL语句
declare
@sql
varchar
(
8000
)
set
@sql
=
'
select name 姓名
'
select
@sql
=
@sql
+
'
, max(case course when
'''
+
course
+
'''
then scores else 0 end)
'
+
course
+
''
from
(
select
distinct
course
from
tb)
as
a
set
@sql
=
@sql
+
'
from tb group by name
'
print
@sql
动态拼接后的语句与我们之前手写的结果一样, 然后通过系统存储过程sp_executesql执行, 进行sql查询, 效果一样, 这里用的一个技巧是select @sql变量赋值, 获取去重后所有的科目的集合, 然后循环动态构造case when语句添加到@sql变量后面, 达到我们想要的效果, 这样也能正确执行. 下次再来看看SQL 2005中的实现