SQL 行转列,列转行 SQL2000/2005分别写法

本文详细介绍了SQL中行转列与列转行的操作,包括数据表的创建、查询和转换技巧,以及如何实现数据的灵活展示。通过实例演示了如何在不同场景下运用SQL进行数据的行列转换,以满足数据分析和报表展示的需求。

--行转列(SQL2005)
--建表
create table test(编号 int,姓名 varchar(20),季度 int,销售额 int)
insert into test values(1,'simon',1,1000)
insert into test values(1,'simon',2,2000)
insert into test values(1,'simon',3,3000)
insert into test values(1,'simon',4,4000)
insert into test values(2,'meme',1,5000)
insert into test values(2,'meme',2,6000)
insert into test values(2,'meme',3,7000)
insert into test values(2,'meme',4,8000)
--执行普通查询
select * from test
--执行转换查询
select 编号,姓名,[1] as "一季度",[2] as "二季度",[3] as "三季度",[4] as "四季度",[5] as "随便1" from test pivot(sum(销售额) for 季度 in([1],[2],[3],[4],[5]))as pvt
--行转列(SQL2000)
/*
普通行列转换
(爱新觉罗.毓华 2007-11-18于海南三亚)

假设有张学生成绩表(tb)如下:
Name Subject Result
张三 语文  74
张三 数学  83
张三 物理  93
李四 语文  74
李四 数学  84
李四 物理  94
*/

-------------------------------------------------------------------------
/*
想变成
姓名         语文        数学        物理         
---------- ----------- ----------- -----------
李四         74          84          94
张三         74          83          93
*/

create table tb
(
   Name    varchar(10) ,
   Subject varchar(10) ,
   Result  int
)

insert into tb(Name , Subject , Result) values('张三' , '语文' , 74)
insert into tb(Name , Subject , Result) values('张三' , '数学' , 83)
insert into tb(Name , Subject , Result) values('张三' , '物理' , 93)
insert into tb(Name , Subject , Result) values('李四' , '语文' , 74)
insert into tb(Name , Subject , Result) values('李四' , '数学' , 84)
insert into tb(Name , Subject , Result) values('李四' , '物理' , 94)
go

--静态SQL,指subject只有语文、数学、物理这三门课程。
select name 姓名,
  max(case subject when '语文' then result else 0 end) 语文,
  max(case subject when '数学' then result else 0 end) 数学,
  max(case subject when '物理' then result else 0 end) 物理
from tb
group by name
/*
姓名         语文        数学        物理         
---------- ----------- ----------- -----------
李四         74          84          94
张三         74          83          93
*/

--动态SQL,指subject不止语文、数学、物理这三门课程。
declare @sql varchar(8000)
set @sql = 'select Name as ' + '姓名'
select @sql = @sql + ' , max(case Subject when ''' + Subject + ''' then Result else 0 end) [' + Subject + ']'
from (select distinct Subject from tb) as a
set @sql = @sql + ' from tb group by name'
exec(@sql)
/*
姓名         数学        物理        语文         
---------- ----------- ----------- -----------
李四         84          94          74
张三         83          93          74
*/

-------------------------------------------------------------------
/*加个平均分,总分
姓名         语文        数学        物理        平均分                总分         
---------- ----------- ----------- ----------- -------------------- -----------
李四         74          84          94          84.00                252
张三         74          83          93          83.33                250
*/

--静态SQL,指subject只有语文、数学、物理这三门课程。
select name 姓名,
  max(case subject when '语文' then result else 0 end) 语文,
  max(case subject when '数学' then result else 0 end) 数学,
  max(case subject when '物理' then result else 0 end) 物理,
  cast(avg(result*1.0) as decimal(18,2)) 平均分,
  sum(result) 总分
from tb
group by name
/*
姓名         语文        数学        物理        平均分                总分         
---------- ----------- ----------- ----------- -------------------- -----------
李四         74          84          94          84.00                252
张三         74          83          93          83.33                250
*/

--动态SQL,指subject不止语文、数学、物理这三门课程。
declare @sql1 varchar(8000)
set @sql1 = 'select Name as ' + '姓名'
select @sql1 = @sql1 + ' , max(case Subject when ''' + Subject + ''' then Result else 0 end) [' + Subject + ']'
from (select distinct Subject from tb) as a
set @sql1 = @sql1 + ' , cast(avg(result*1.0) as decimal(18,2)) 平均分,sum(result) 总分 from tb group by name'
exec(@sql1)
/*
姓名         数学        物理        语文        平均分                总分         
---------- ----------- ----------- ----------- -------------------- -----------
李四         84          94          74          84.00                252
张三         83          93          74          83.33                250
*/

drop table tb   

 


--列转行(SQL2005)
--建表
create table test2(编号 int,姓名 varchar(20), 一季度 int, 二季度 int, 三季度 int, 四季度 int)
insert into test2 values(1,'simon',1000,2000,4000,5000)
insert into test2 values(2,'meme',3000,3500,4200,5500)
--执行普通查询
select * from test2
--执行转换查询
select 编号,姓名,季度,销售额 from test2 unpivot(销售额 for 季度 in(一季度,二季度,三季度,四季度)) as unpvt

--列转行(SQL2000)
create table tb1
(
   姓名 varchar(10) ,
   语文 int ,
   数学 int ,
   物理 int
)

insert into tb1(姓名 , 语文 , 数学 , 物理) values('张三',74,83,93)
insert into tb1(姓名 , 语文 , 数学 , 物理) values('李四',74,84,94)

select * from
(
  select 姓名 as Name , Subject = '语文' , Result = 语文 from tb1  
  union all
  select 姓名 as Name , Subject = '数学' , Result = 数学 from tb1
  union all
  select 姓名 as Name , Subject = '物理' , Result = 物理 from tb1
) t
order by name , case Subject when '语文' then 1 when '数学' then 2 when '物理' then 3 when '总分' then 4 end

 

<think>我们参考用户提供的引用资料,特别是引用[1]和引用[4]中关于行转列的方法,以及引用[2]和引用[3]中关于转行的方法。用户意图:了解SQL中如何实现转行行转列的操作。行转列Pivot):将多行数据转换为一行中的多,常用于汇总数据。转行(Unpivot):将一行中的多数据转换为多行,常用于数据分解。我们将分别介绍这两种操作的实现方法,并给出示例。一、行转列Pivot)方法1:使用CASE WHEN和GROUPBY(通用方法,适用于所有SQL数据库)示例数据:假设有表student1,包含name, subject, score三个字段,数据如下:name|subject |score------|-------| -----张三|语文|65张三|数学|78张三|英语|93李四|语文|76李四|数学|87李四|英语|90我们想要将每个学生的各科成绩转换成一行,即每行显示一个学生的所有科目成绩。SQL语句如下:```sqlSELECT name,MAX(CASE WHENsubject='语文'THEN scoreELSE0 END) AS"语文",MAX(CASE WHENsubject='数学'THEN scoreELSE0 END) AS"数学",MAX(CASE WHENsubject='英语'THEN scoreELSE0 END) AS"英语" FROMstudent1GROUPBY name; ```这里使用MAX函数是因为在分组后,每个学生每个科目只有一条记录(如果有多个则取最大值,如果只有一个则就是这个值)。也可以使用SUM,但需要确保每个科目每个学生只有一条记录。方法2:使用数据库特定的PIVOT函数(如SQLServer、Oracle等支持) SQLServer示例: ```sqlSELECT *FROM(SELECTname,subject,score FROMstudent1) ASsourceTablePIVOT(MAX(score)FOR subjectIN (语文,数学,英语))AS pivotTable;```二、转行(Unpivot)方法1:使用UNION ALL(通用方法)示例数据:假设有表tb_colrow,包含name,Apple,Banana,Orange,Pear字段,数据如下:name|Apple |Banana |Orange |Pear------| -----| ------| ------| -----张三|10|20|30|40李四|15|25|35|45我们想要将每个水果的转成行,即每个学生每个水果一行。 SQL语句如下: ```sqlSELECT name, 'Apple'AS fruit, AppleAS amountFROM tb_colrowUNION ALLSELECTname,'Banana',Banana FROMtb_colrowUNIONALLSELECT name, 'Orange',Orange FROMtb_colrowUNIONALLSELECT name, 'Pear',Pear FROMtb_colrowORDER BYname,fruit;```方法2:使用数据库特定的UNPIVOT函数(如SQL Server、Oracle等支持) SQLServer示例: ```sqlSELECT name, fruit, amountFROMtb_colrowUNPIVOT(amountFOR fruitIN (Apple,Banana,Orange,Pear)) ASunpivotTable;```另外,引用[3]中还提到了一种使用XML转换的方法(适用于SQLServer),但相对复杂,这里不详细展开。注意事项:1.行转列时,需要明确知道要转置的名(如语文、数学、英语),如果名是动态的,可能需要使用动态SQL。2.转行时,同样需要明确名,如果名很多,使用UNIONALL会比较冗长,但通用性好。以上是SQL中实现行转列转行的常用方法。具体使用哪种方法取决于数据库系统以及具体需求。根据问题,我们生成以下相关问题:§§相关问题§§1.行转列操作中,如果科目是动态变化的(即不确定有多少个科目),如何实现? 2.转行操作中,如果非常多,有什么简便的写法?3.在MySQL中如何实现行转列转行? 4.行转列转行在性能上有什么需要注意的地方?</think>### SQL中的行转列转行操作 行转列Pivot)和转行(Unpivot)是SQL中常用的数据转换技术,主要用于报表生成和数据预处理。以下是具体实现方法: #### 一、行转列Pivot) **场景**:将多行数据按类别转换为单行的多(如学生各科成绩横向展示) **方法1:CASE WHEN + GROUP BY(通用方案)** ```sql SELECT name, MAX(CASE WHEN subject='语文' THEN score END) AS "语文", MAX(CASE WHEN subject='数学' THEN score END) AS "数学", MAX(CASE WHEN subject='英语' THEN score END) AS "英语" FROM student_x GROUP BY name; ``` *结果示例:* | name | 语文 | 数学 | 英语 | |------|------|------|------| | 张三 | 65 | 78 | 93 | | 李四 | 76 | 87 | 90 | **方法2:PIVOT函数(SQL Server/Oracle专用)** ```sql SELECT * FROM student_x PIVOT (MAX(score) FOR subject IN ('语文','数学','英语')); ``` #### 二、转行(Unpivot) **场景**:将多数据转换为多行(如水果销售数据纵向展示) **方法1:UNION ALL(通用方案)** ```sql SELECT name, 'Apple' AS fruit, Apple AS amount FROM tb_colrow UNION ALL SELECT name, 'Banana', Banana FROM tb_colrow UNION ALL SELECT name, 'Orange', Orange FROM tb_colrow; ``` *结果示例:* | name | fruit | amount | |------|--------|--------| | 张三 | Apple | 10 | | 张三 | Banana | 20 | | 李四 | Apple | 15 | **方法2:UNPIVOT函数(SQL Server/Oracle专用)** ```sql SELECT name, fruit, amount FROM tb_colrow UNPIVOT (amount FOR fruit IN (Apple, Banana, Orange)); ``` #### 三、特殊场景处理 1. **动态名转换**(如不固定科目数量) 需使用存储过程动态生成SQL,通过`EXECUTE`执行(引用[3]的XML方法适用于SQL Server): ```sql DECLARE @cols NVARCHAR(MAX), @sql NVARCHAR(MAX); SELECT @cols = STRING_AGG(QUOTENAME(subject), ',') FROM (SELECT DISTINCT subject FROM student_x) t; SET @sql = N'SELECT name, ' + @cols + ' FROM student_x PIVOT (MAX(score) FOR subject IN (' + @cols + ')) p'; EXEC sp_executesql @sql; ``` 2. **多值字段拆解**(如技能表拆分) 使用字符串函数(引用[3]): ```sql SELECT name, skill_split FROM ( SELECT name, CAST('<root><v>' + REPLACE(skill, '-', '</v><v>') + '</v></root>' AS XML) AS xml_data FROM character_skills ) t CROSS APPLY ( SELECT skill_split = N.v.value('.', 'varchar(100)') FROM t.xml_data.nodes('/root/v') N(v) ) s; ``` > **性能建议**: > - 行转列优先使用`MAX()`聚合避免空值干扰(引用[4]) > - 大数据量时转行用`UNION ALL`替代`UNION` > - 定期更新统计信息以优化执行计划
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值