最近,在论坛中,遇到了不少比较难的sql问题,虽然自己都能解决,但发现过几天后,就记不起来了,也忘记解决的方法了。

所以,觉得有必要记录下来,这样以后再次碰到这类问题,也能从中获取解答的思路。

下面的几个问题,都是动态行转列的问题。

1、数据查询,行转列的问题。

http://bbs.csdn.net/topics/390621630?page=1#post-395855019

根据数据查询得到如下数据表(tab):

班级           学号     姓名        科目             得分          班排名       校排名         标准分
ClassName  Code  Name  SubjectName TotalScore Cla***ank SchoolRank TValue
201班        101     张三      语文               95             1                 1           700
201班        102     李四      语文               83             2                 3           600
202班        201     张飞      语文               85             1                 2           700
202班        202     赵云      语文               75             2                 4           600
201班        101     张三      数学               83             2                 2           600
201班        102     李四      数学               85             1                 3           700
202班        201     张飞      数学               95             1                 1           700
202班        202     赵云      数学               80             2                 4           600
需要得到如下数据:
班级           学号     姓名    语文   得分  班排名  校排名  标准分 数学   得分  班排名    校排名    标准分
201班        101     张三      语文     95       1        1       700       数学    83        2                 2           600
201班        102     李四      语文     83       2        3       600       数学    85        1                 3           700
202班        201     张飞      语文     85       1        2       700       数学    95        1                 1           700
202班        202     赵云      语文     75       2        4       600       数学    80        2                 4           600
真心求SQL,或者实现方法。
科目是动态的。能实现静态转换也行。
真心谢谢。

我的解法:

if object_id('tab') is not null drop table tab go  create table tab ( [班级] varchar(9),[学号] varchar(4),[姓名] varchar(4), [科目] varchar(11),[得分] varchar(10),[班排名] varchar(9), [校排名] varchar(10),[标准分] varchar(6) )  insert tab select '201班','101','张三','语文','95','1','1','700' union all select '201班','102','李四','语文','83','2','3','600' union all select '202班','201','张飞','语文','85','1','2','700' union all select '202班','202','赵云','语文','75','2','4','600' union all select '201班','101','张三','数学','83','2','2','600' union all select '201班','102','李四','数学','85','1','3','700' union all select '202班','201','张飞','数学','95','1','1','700' union all select '202班','202','赵云','数学','80','2','4','600' --------------开始查询--------------------------   declare @sql nvarchar(3000);  set @sql = '';  select @sql =     @sql + ',min(case when 科目=''' + 科目 + ''' then 科目 else null end) as ['+科目+ ']'+           ',min(case when 科目=''' + 科目 + ''' then 得分 else null end) as 得分' +           ',min(case when 科目=''' + 科目 + ''' then 班排名 else null end) as 班排名'+           ',min(case when 科目=''' + 科目 + ''' then 校排名 else null end) as 校排名'+           ',min(case when 科目=''' + 科目 + ''' then 标准分 else null end) as 标准分'             from tab group by 科目 order by 科目 desc  select @sql = 'select 班级,学号,姓名'+@sql +               ' from tab group by 班级,学号,姓名'  select @sql        exec(@sql)        /* 班级        学号   姓名   语文          得分         班排名       校排名        标准分    数学          得分         班排名       校排名        标准分 --------- ---- ---- ----------- ---------- --------- ---------- ------ ----------- ---------- --------- ---------- ------ 201班      101  张三   语文          95         1         1          700    数学          83         2         2          600 201班      102  李四   语文          83         2         3          600    数学          85         1         3          700 202班      201  张飞   语文          85         1         2          700    数学          95         1         1          700 202班      202  赵云   语文          75         2         4          600    数学          80         2         4          600 警告: 聚合或其他 SET 操作消除了 Null 值。  */
上面语句,产生的动态语句:

select 班级,学号,姓名,         min(case when 科目='语文' then 科目 else null end) as [语文],        min(case when 科目='语文' then 得分 else null end) as 得分,        min(case when 科目='语文' then 班排名 else null end) as 班排名,        min(case when 科目='语文' then 校排名 else null end) as 校排名,        min(case when 科目='语文' then 标准分 else null end) as 标准分,        min(case when 科目='数学' then 科目 else null end) as [数学],        min(case when 科目='数学' then 得分 else null end) as 得分,        min(case when 科目='数学' then 班排名 else null end) as 班排名,        min(case when 科目='数学' then 校排名 else null end) as 校排名,        min(case when 科目='数学' then 标准分 else null end) as 标准分  from tab  group by 班级,学号,姓名
2、sql语句,某一行数据变为列名。
http://bbs.csdn.net/topics/390622184?page=1#post-395860916
我有一个表
   日期       合肥   F3      F4       芜湖   F6      F7
--------------------------------------------------------------------
   NULL       气象   最高    最低     气象   最高    最低
   2013-10    多云   32      20       雨     35      20
   2013-11    晴     30      20       晴     25      20

现在要变为:

   日期           城市    气象    最高     最低
--------------------------------------------------------------
   2013-10        合肥    多云    32        20
   2013-10        芜湖    雨      35        20
   2013-11        合肥    晴      30        20
   2013-11        芜湖    晴      25        20
可以的话尽量写成动态,因为不只两个城市
如果我的第一个表是这样呢:
   日期       合肥   F3      F4       芜湖   F6      F7     北京   F9    F10    福州   F12  F13
------------------------------------------------------------------------------------------------
   NULL       气象   最高    最低     气象   最高    最低   气象   最高   最低  气象   最高  最低
   2013-10    多云   32      20       雨     35      20      晴     32     20   多云   25    20
   2013-11    晴     30      20       晴     25      20      雨     31     21   晴     25    21
你能否写一个循环语句,首先城市的数量是(最后一个列名的阿拉伯数字-1)/3,然后再作判断写出像你第一个贴得出的结果集。

我的解法:
if object_id('tb') is not null    drop table tb go  create table tb ( 日期 varchar(15), 合肥 varchar(10), F3 varchar(10), F4 varchar(10), 芜湖 varchar(10), F6 varchar(10), F7 varchar(10), 北京 varchar(10), F9 varchar(10), F10 varchar(10), 福州 varchar(10), F12 varchar(10), F13 varchar(10) )  insert into tb select NULL,'气象', '最高','最低','气象','最高','最低','气象','最高','最低' ,'气象','最高','最低' union all select '2013-10','多云', '32' ,'20', '雨','35', '20', '晴','32','20' , '多云','25','20' union all select '2013-11','晴',  '30','20','晴','25', '20', '雨','31','21','晴','25' , '21'  go   declare @tb_name nvarchar(100); declare @t table(tb_name nvarchar(100),column_name nvarchar(100),column_id int)   declare @i int declare @count int declare @sql nvarchar(max);  --这里的表为 tb,需要换成你自己的表名 set @tb_name = 'tb'  insert into @t select t.name ,        c.name,        c.column_id from sys.tables t inner join sys.columns c         on t.object_id = c.object_id where t.name = @tb_name     set @i = 2 set @count = (select count(*) from @t) set @sql ='';  while @i < @count begin    set @sql = @sql + 'union all select 日期,''' +                (select column_name from @t where column_id = @i) + ''' as 城市,' +               (select column_name from @t where column_id = @i) + ' as 气象,' +                (select column_name from @t where column_id = @i+1) + ' as 最高,'+               (select column_name from @t where column_id = @i+1+1) + ' as 最低 ' +               ' from ' + (select distinct tb_name from @t) + ' where 日期 is not null '                   set @i = @i + 3 end  select @sql = stuff(@sql,1,len('union all'),'')  --select @sql  exec(@sql) /* 日期              城市   气象         最高         最低 --------------- ---- ---------- ---------- ---------- 2013-10         合肥   多云         32         20 2013-11         合肥   晴          30         20 2013-10         芜湖   雨          35         20 2013-11         芜湖   晴          25         20 2013-10         北京   晴          32         20 2013-11         北京   雨          31         21 2013-10         福州   多云         25         20 2013-11         福州   晴          25         21 */
动态生成的sql语句:
select 日期,'合肥' as 城市,合肥 as 气象,F3 as 最高,F4 as 最低    from tb where 日期 is not null   union all   select 日期,'芜湖' as 城市,芜湖 as 气象,F6 as 最高,F7 as 最低    from tb where 日期 is not null   union all   select 日期,'北京' as 城市,北京 as 气象,F9 as 最高,F10 as 最低    from tb where 日期 is not null   union all   select 日期,'福州' as 城市,福州 as 气象,F12 as 最高,F13 as 最低    from tb where 日期 is not null

3、sql经典行转列问题,外加点小难度。

http://bbs.csdn.net/topics/390636728

脚本如下:

create table tab ( 	NumID int, 	SkuID int, 	Stock int ) go declare @i int set @i=1 while @i<23 begin insert into tab (NumID,SkuID,Stock) values ('23940',3982+@i,3+@i) set @i=@i+1 end declare @k int set @k=1 while @k<4 begin insert into tab (NumID,SkuID,Stock) values ('33950',32+@k,3+@k) set @k=@k+1 end declare @n int set @n=1 while @n<2 begin insert into tab (NumID,SkuID,Stock) values ('36951',52+@n,3+@n) set @n=@n+1 end
求结果如下,注意:相同NumID中超过20条记录以后行转列过程中需要折分成两条记录:

NumID  Sku_Stock ------------------------- 23940  3983:4;3984:5;3985:6;3986:7;3987:8;3988:9;3989:10;3990:11;3991:12;3992:13;3993:14;3994:15;3995:16;3996:17;3997:18;3998:19;3999:20;4000:21;4001:22;4002:23 23940  4003:24;4004:25 33950  33:4;34:5;35:6 36951  53:4
我的解法:

;WITH t AS (     SELECT *,                        (select count(*) from tab t2             where t2.numid = t1.numid and                    t2.skuid <=t1.skuid) as rownum     FROM tab t1 )  ,tt AS (     select NumID,SkuID,Stock,rownum,            (rownum -1) / 20  as rownum_group     from t )  SELECT NumID,        STUFF(               (                 SELECT ';'+cast(SkuID as varchar)+':'+cast(Stock as varchar)                 FROM tt tt2                 WHERE tt2.NumID=tt1.NumID AND                        tt2.rownum_group = tt1.rownum_group                  FOR XML PATH('')                ),                1,1,''             ) as sku_stock FROM tt tt1 GROUP BY NumID,rownum_group /* NumID	sku_stock 23940	3983:4;3984:5;3985:6;3986:7;3987:8;3988:9;3989:10;3990:11;3991:12;3992:13;3993:14;3994:15;3995:16;3996:17;3997:18;3998:19;3999:20;4000:21;4001:22;4002:23 23940	4003:24;4004:25 33950	33:4;34:5;35:6 36951	53:4 */