行转列问题总结
-
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