在SQL中有一类需求,需要行遍列。

   通用的方法是用 case when语句,但是如果行很多,或者行不定时,或者指标列很多,这种方法就无法适应。case when只适应于静态的行变列转换。
   Pivot方法能够同动态行变列,但还是有个缺点,只能出一个指标,比如demo的只有分数一个指标,假如还有多个指标的话,需要多个SQL拼接也倒是可行。

   

问题:

对于动态的转换有一个相对简单灵活的方法

问题:假设有张学生成绩表(tb)如下:

姓名 课程 分数
张三 语文 74
张三 数学 83
张三 物理 93
李四 语文 74
李四 数学 84
李四 物理 94

想变成(得到如下结果): 
姓名 语文 数学 物理 
李四 74   84   94
张三 74   83   93


用静态的转换方法:

--SQL SERVER 2000 动态SQL,指课程不止语文、数学、物理这三门课程。(以下同)
declare @sql varchar(8000)
set @sql = 'select 姓名 '
select @sql = @sql + ' , max(case 课程 when ''' + 课程 + ''' then 分数 else 0 end) [' + 课程 + ']'
from (select distinct 课程 from tb) as a
set @sql = @sql + ' from tb group by 姓名'
exec(@sql) 
--通过动态构建@sql,得到如下脚本
select 姓名 as 姓名 ,
  max(case 课程 when '语文' then 分数 else 0 end) 语文,
  max(case 课程 when '数学' then 分数 else 0 end) 数学,
  max(case 课程 when '物理' then 分数 else 0 end) 物理
from tb
group by 姓名

--SQL SERVER 2005 动态SQL。
declare @sql varchar(8000)
select @sql = isnull(@sql + '],[' , '') + 课程 from tb group by 课程
set @sql = '[' + @sql + ']'
exec ('select * from (select * from tb) a pivot (max(分数) for 课程 in (' + @sql + ')) b')
--得到SQL SERVER 2005 静态SQL。
select * from (select * from tb) a pivot (max(分数) for 课程 in (语文,数学,物理)) b

我的方法是:

if object_id('tempdb.dbo.#tmp_cmp')is not null drop table #tmp_cmp
Select [姓名] Into #tmp_cmp From tb group by [姓名]
	
Declare @dim2 varchar(255)
Declare Cur Cursor for 
Select [课程] From  tb  group by [课程]
Open Cur
Fetch next From Cur into @dim2 
while @@FETCH_STATUS=0
begin
		exec ( ' Alter table #tmp_cmp Add [分数_'+ @dim2 + '] int ') 

		exec ( ' Update #tmp_cmp Set 
		[分数_'+ @dim2 + '] =tlb.[分数]
		From #tmp_cmp tla inner Join tb tlb on tla.[姓名]=tlb.[姓名] where tlb.[课程]='''+@dim2+''' ') 
		Fetch next From Cur into @dim2 
End
Close Cur
Deallocate Cur
		
Select * From #tmp_cmp

这个方法写法不是很优雅,不过对付多指标的情况,还是比较方便的。