sql server行转为列的问题

 

行转列问题总结  -   1 、行转列 (后面不断整理论坛中出现的各类问题)

--- 1 、最简单的行转列
/*     

问题:假设有张学生成绩表(tb)如下:
姓名 课程 分数
张三 语文 74
张三 数学 83
张三 物理 93
李四 语文 74
李四 数学 84
李四 物理 94


想变成(得到如下结果): 
姓名 语文 数学 物理 
李四 74   84   94
张三 74   83   93
*/
-- 测试用
IF OBJECT_ID(
' [tb] ' ) IS NOT NULL DROP TABLE [tb]
GO
create table tb(姓名 varchar(
10 ) , 课程 varchar( 10 ) , 分数  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

-- 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 分数   end) 语文,
  max(
case  课程 when  ' 数学 '  then 分数   end) 数学,
  max(
case  课程 when  ' 物理 '  then 分数   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

-- 查询结果
/*
姓名         数学          物理          语文          
---------- ----------- ----------- ----------- 
李四         84          94          74
张三         83          93          74

(所影响的行数为 2 行)
*/


-- 2  加合计
/*
问题:在上述结果的基础上加平均分,总分,得到如下结果:
姓名 语文 数学 物理 平均分 总分 
---- ---- ---- ---- ------ ----
李四 74   84   94   84.00  252
张三 74   83   93   83.33  250
*/

-- SQL SERVER  2000  静态SQL。
select 姓名 姓名,
  max(
case  课程 when  ' 语文 '  then 分数   end) 语文,
  max(
case  课程 when  ' 数学 '  then 分数   end) 数学,
  max(
case  课程 when  ' 物理 '  then 分数   end) 物理,
  cast(avg(分数
* 1.0 as   decimal ( 18 , 2 )) 平均分,
  sum(分数) 总分
from tb
group by 姓名

-- 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  +   '  , cast(avg(分数*1.0) as decimal(18,2)) 平均分 , sum(分数) 总分 from tb group by 姓名 '
exec(@sql) 

-- SQL SERVER  2005  静态SQL。
select m.
*  , n.平均分 , n.总分 from
(select 
*  from (select  *  from tb) a pivot (max(分数)  for  课程  in  (语文,数学,物理)) b) m,
(select 姓名 , cast(avg(分数
* 1.0 as   decimal ( 18 , 2 )) 平均分 , sum(分数) 总分 from tb group by 姓名) n
where  m.姓名  =  n.姓名

-- SQL SERVER  2005  动态SQL。
declare @sql varchar(
8000 )
select @sql 
=  isnull(@sql  +   ' , '  ,  '' +  课程 from tb group by 课程
exec (
' select m.* , n.平均分 , n.总分 from
(select  *  from (select  *  from tb) a pivot (max(分数)  for  课程  in  ( '  + @sql +  ' )) b) m , 
(select 姓名 , cast(avg(分数
* 1.0 as   decimal ( 18 , 2 )) 平均分 , sum(分数) 总分 from tb group by 姓名) n
where  m.姓名  =  n.姓名 ' )

其他实例

http:
// topic.csdn.net/u/20100708/18/55df5a90-27a7-4452-a69a-27f735539a1f.html?seed=24842417&r=66831902#r_66831902


-- 3 、不同数据按照序号转为列,方法基本同  1

if  object_id( ' tb1 ' is  not  null  drop table tb1
go
CREATE table tb1 
-- 数据表
(
cpici varchar(
10 ) not  null ,
cname varchar(
10 ) not  null ,
cvalue 
int   null  
)
-- 插入测试数据
INSERT INTO tb1 values(
' T501 ' , ' x1 ' , 31 )
INSERT INTO tb1 values(
' T501 ' , ' x1 ' , 33 )
INSERT INTO tb1 values(
' T501 ' , ' x1 ' , 5 )

INSERT INTO tb1 values(
' T502 ' , ' x1 ' , 3 )
INSERT INTO tb1 values(
' T502 ' , ' x1 ' , 22 )
INSERT INTO tb1 values(
' T502 ' , ' x1 ' , 3 )

INSERT INTO tb1 values(
' T503 ' , ' x1 ' , 53 )
INSERT INTO tb1 values(
' T503 ' , ' x1 ' , 44 )
INSERT INTO tb1 values(
' T503 ' , ' x1 ' , 50 )
INSERT INTO tb1 values(
' T503 ' , ' x1 ' , 23 )


-- 在sqlserver2000里需要用自增辅助
alter table tb1 add id 
int  identity
go
declare @s varchar(
8000 )
set  @s = ' select cpici  '
select @s
= @s + ' ,max(case when rn= ' + ltrim(rn) + '  then cvalue end) as cvlue ' + ltrim(rn)
from (select distinct rn from (select rn
= (select count( 1 ) from tb1  where  cpici = t.cpici and id <= t.id) from tb1 t)a)t
set  @s = @s + '  from (select rn=(select count(1) from tb1 where cpici=t.cpici and id<=t.id),* from tb1 t
) t group by cpici '

exec(@s)
go
alter table tb1 drop column id 

-- 再2005就可以用row_number
declare @s varchar(
8000 )
set  @s = ' select cpici  '
select @s
= @s + ' ,max(case when rn= ' + ltrim(rn) + '  then cvalue end) as cvlue ' + ltrim(rn)
from (select distinct rn from (select rn
= row_number()over(partition by cpici order by getdate()) from tb1)a)t
set  @s = @s + '  from (select rn=row_number()over(partition by cpici order by getdate()),* from tb1
) t group by cpici '

exec(@s)

--- 结果
/*
cpici      cvlue1      cvlue2      cvlue3      cvlue4
---------- ----------- ----------- ----------- -----------
T501       31          33          5           NULL
T502       3           22          3           NULL
T503       53          44          50          23
警告: 聚合或其他 SET 操作消除了空值。

(3 行受影响)

*/


-- 测试用
IF OBJECT_ID(
' [tb] ' ) IS NOT NULL DROP TABLE [tb]
GO
create table tb(电话号码 varchar(
15 ), 通话时长  int  ,行业 varchar( 10 ))
insert tb
select 
' 13883633601 ' 10  , ' 餐饮 '  union all
select 
' 18689704236 ' 20  , ' 物流 '  union all
select 
' 13883633601 ' 20  , ' 物流 '  union all
select 
' 13883633601 ' 20  , ' 汽车 '  union all
select 
' 18689704236 ' 20  , ' 医疗 '  union all
select 
' 18689704236 ' 20  , ' it '  union all
select 
' 18689704236 ' 20  , ' 汽车 '  union all
select 
' 13883633601 ' 50  , ' 餐饮 '
go

declare @sql varchar(
8000 )
set  @sql = ' select 电话号码,sum(通话时长) 通话总和 '
select @sql
= @sql + ' ,max(case when rowid= ' + ltrim(rowid) + '  then 行业 else  ''''  end) as [行业 ' + ltrim(rowid) + ' ] '
from (select distinct rowid from (select (select count(distinct 行业) from tb 
where  电话号码 = t.电话号码 and 行业 <= t.行业) rowid
from tb t) a) b
set  @sql = @sql + '  from ( select * , (select count(distinct 行业) from tb where 电话号码=t.电话号码 and 行业<=t.行业) rowid
from tb t ) t group by 电话号码 '
exec(@sql)

-- 结果
/*

(所影响的行数为 8 行)

电话号码            通话总和        行业1        行业2        行业3        行业4        
--------------- ----------- ---------- ---------- ---------- ---------- 
13883633601     100         餐饮         汽车         物流         
18689704236     80          it         汽车         物流         医疗

(所影响的行数为 2 行)

*/

另一种动态行转列:

http:
// topic.csdn.net/u/20100612/10/4CFCB667-89FA-4985-90D5-B8A420A6FF12.html

if  object_id( ' [tb] ' is  not  null  drop table [tb]
go   
create table [tb]([姓名] varchar(
1 ),[部门] varchar( 4 ),[学历] varchar( 4 ),[出生年月] datetime)
insert [tb]
select 
' A ' , ' 后勤 ' , ' 高中 ' , ' 1986-1-1 '  union all
select 
' B ' , ' 后勤 ' , ' 初中 ' , ' 1984-3-7 '  union all
select 
' C ' , ' 管理 ' , ' 本科 ' , ' 1987-2-1 '  union all
select 
' D ' , ' 操作 ' , ' 专科 ' , ' 1976-2-1 '  union all
select 
' E ' , ' 操作 ' , ' 专科 ' , ' 1943-2-1 '    
go


GO
if  object_id( ' GetGroupByCol ' is  not  null  drop proc GetGroupByCol
go
create  PROCEDURE [dbo].[GetGroupByCol]
@colm nvarchar(
100 )
  AS
declare @sql varchar(
4000 )

set  @sql = '
declare @sql varchar( 8000 )
set  @sql = '' select 部门 ''
select @sql 
= @sql +   '' , sum( case  ltrim( ' +@colm+ ' ) when  '''''' + ltrim( '  + @colm +  ' ) + ''''''  then  1   else   0  end) 
[
'' + ltrim( '  + @colm +  ' ) + '' ] ''  from (select distinct  ' +@colm+ '  from tb  where   ' +@colm+ '   is  not  null as  a
set  @sql  =  @sql  +   ''  from tb group by 部门 ''
exec(@sql)
'

exec(@sql)
GO

exec GetGroupByCol N
' 学历 '
exec GetGroupByCol N
' 出生年月 '
exec GetGroupByCol N
' 姓名 '

/*

(所影响的行数为 5 行)

部门   本科          初中          高中          专科          
---- ----------- ----------- ----------- ----------- 
操作   0           0           0           2
管理   1           0           0           0
后勤   0           1           1           0

(所影响的行数为 3 行)

部门   02  1 1943 12:00AM 02  1 1976 12:00AM 03  7 1984 12:00AM 01  1 1986 12:00AM 02  1 1987 12:00AM 
---- ------------------ ------------------ ------------------ ------------------ ------------------ 
操作   1                  1                  0                  0                  0
管理   0                  0                  0                  0                  1
后勤   0                  0                  1                  1                  0

(所影响的行数为 3 行)

部门   A           B           C           D           E           
---- ----------- ----------- ----------- ----------- ----------- 
操作   0           0           0           1           1
管理   0           0           1           0           0
后勤   1           1           0           0           0

(所影响的行数为 3 行)
*/


以下可参考的例子

1 、普通多表联合

http:
// topic.csdn.net/u/20100623/00/077055eb-784d-4b27-8407-2c17adc06c60.html?seed=81934135&r=66426155#r_66426155

http:
// topic.csdn.net/u/20100622/19/9710803c-441b-45d0-b010-703a2633fe89.html?47161

2 、多表根据时间 计算序号
http:
// topic.csdn.net/u/20100623/12/bbb0921b-0e1b-4435-8e85-959d87844954.html?seed=2145286087&r=66438763#r_66438763
http: // topic.csdn.net/u/20100701/09/1684649b-b893-463b-8b40-7f4b894cd41e.html?seed=205688256&r=66630774#r_66630774

3 、财务相关
http:
// topic.csdn.net/u/20100626/00/83499112-43ae-4caa-a1fd-268cc5138da6.html?seed=415671352&r=66513615#r_66513615

4 、根据行数转列

http:
// topic.csdn.net/u/20100705/12/e325571b-c368-4174-859f-17ae708eca3d.html

http:
// topic.csdn.net/u/20100706/09/c34728dc-6167-45df-b7cf-974612b9aa8b.html

http:
// topic.csdn.net/u/20100706/16/f217deed-a2be-4950-b911-2624ac7a881a.html?39445

5 、根据排序大小转

http:
// topic.csdn.net/u/20100707/13/63f4a02e-ebc3-4c71-9380-d6b2ca0eb366.html?39970

6 、分组排序按序号转

http:
// topic.csdn.net/u/20100725/05/7f813114-c423-4759-97b8-b22e1e2e90d7.html?seed=471594449&r=67220945#r_67220945

 

转载于:https://www.cnblogs.com/shizhi57/archive/2011/05/17/2048928.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值