MySQL 实现行转列SQL

MySQL 实现行转列SQL概述好久没写SQL语句,今天看到问答中的一个问题,拿来研究一下。问题链接:关于Mysql的分级输出问题情景简介学校里面记录成绩,每个人的选课不一样,而且以后会添加课程,所以不需要把所有课程当作列。数据表里面数据如下图,使用姓名+课程作为联合主键(有些需求可能不需要联合主键)。本文以MySQL为基础,其他数据库会有些许语法不同。数据库表数据:处理后的结果(行转列):方法一:这里可以使用Max,也可…

大家好,又见面了,我是你们的朋友全栈君。

概述

好久没写SQL语句,今天看到问答中的一个问题,拿来研究一下。

问题链接:关于Mysql 的分级输出问题

情景简介

学校里面记录成绩,每个人的选课不一样,而且以后会添加课程,所以不需要把所有课程当作列。数据表里面数据如下图,使用姓名+课程作为联合主键(有些需求可能不需要联合主键)。本文以MySQL为基础,其他数据库会有些许语法不同。

数据库表数据:

MySQL 实现行转列SQLMySQL 实现行转列SQL

处理后的结果(行转列):

MySQL 实现行转列SQL
MySQL 实现行转列SQL

方法一:

这里可以使用Max,也可以使用Sum;

注意第二张图,当有学生的某科成绩缺失的时候,输出结果为Null; 

[sql] 
view plain  
copy

  1. SELECT  
  2.     SNAME,  
  3.     MAX(  
  4.         CASE CNAME  
  5.         WHEN ‘JAVA’ THEN  
  6.             SCORE  
  7.         END  
  8.     ) JAVA,  
  9.     MAX(  
  10.         CASE CNAME  
  11.         WHEN ‘mysql’ THEN  
  12.             SCORE  
  13.         END  
  14.     ) mysql  
  15. FROM  
  16.     stdscore  
  17. GROUP BY  
  18.     SNAME;  



可以在第一个Case中加入Else语句解决这个问题:

[sql] 
view plain  
copy

  1. SELECT  
  2.     SNAME,  
  3.     MAX(  
  4.         CASE CNAME  
  5.         WHEN ‘JAVA’ THEN  
  6.             SCORE  
  7.         ELSE  
  8.             0  
  9.         END  
  10.     ) JAVA,  
  11.     MAX(  
  12.         CASE CNAME  
  13.         WHEN ‘mysql’ THEN  
  14.             SCORE  
  15.         ELSE  
  16.             0  
  17.         END  
  18.     ) mysql  
  19. FROM  
  20.     stdscore  
  21. GROUP BY  
  22.     SNAME;  

方法二:

[sql] 
view plain  
copy

  1. SELECT DISTINCT  a.sname,  
  2. (SELECT score FROM stdscore b WHERE a.sname=b.sname AND b.CNAME=‘JAVA’ ) AS ‘JAVA’,  
  3. (SELECT score FROM stdscore b WHERE a.sname=b.sname AND b.CNAME=‘mysql’ ) AS ‘mysql’  
  4. FROM stdscore a  



方法三:

[sql] 
view plain  
copy

  1. DROP PROCEDURE  
  2. IF EXISTS sp_score;  
  3. DELIMITER &&  
  4.   
  5. CREATE PROCEDURE sp_score ()  
  6. BEGIN  
  7.     #课程名称  
  8.     DECLARE  
  9.         cname_n VARCHAR (20) ; #所有课程数量  
  10.         DECLARE  
  11.             count INT ; #计数器  
  12.             DECLARE  
  13.                 i INT DEFAULT 0 ; #拼接SQL字符串  
  14.             SET @s = ‘SELECT sname’ ;  
  15.             SET count = (  
  16.                 SELECT  
  17.                     COUNT(DISTINCT cname)  
  18.                 FROM  
  19.                     stdscore  
  20.             ) ;  
  21.             WHILE i < count DO  
  22.   
  23.   
  24.             SET cname_n = (  
  25.                 SELECT  
  26.                     cname  
  27.                 FROM  
  28.                     stdscore  
  29.                 GROUP BY CNAME   
  30.                 LIMIT i,  
  31.                 1  
  32.             ) ;  
  33.             SET @s = CONCAT(  
  34.                 @s,  
  35.                 ‘, SUM(CASE cname WHEN ‘,  
  36.                 ‘\”,  
  37.                 cname_n,  
  38.                 ‘\”,  
  39.                 ‘ THEN score ELSE 0 END)’,  
  40.                 ‘ AS ‘,  
  41.                 ‘\”,  
  42.                 cname_n,  
  43.                 ‘\”  
  44.             ) ;  
  45.             SET i = i + 1 ;  
  46.             END  
  47.             WHILE ;  
  48.             SET @s = CONCAT(  
  49.                 @s,  
  50.                 ‘ FROM stdscore GROUP BY sname’  
  51.             ) ; #用于调试  
  52.             #SELECT @s;  
  53.             PREPARE stmt  
  54.             FROM  
  55.                 @s ; EXECUTE stmt ;  
  56.             END&&  
  57.   
  58. CALL sp_score () ;  


处理后的结果(行转列)分级输出:

MySQL 实现行转列SQL
MySQL 实现行转列SQL




方法一:

这里可以使用Max,也可以使用Sum;

注意第二张图,当有学生的某科成绩缺失的时候,输出结果为Null; 

[sql] 
view plain  
copy

  1. SELECT  
  2.     SNAME,  
  3.     MAX(  
  4.         CASE CNAME  
  5.         WHEN ‘JAVA’ THEN  
  6.             (  
  7.                 CASE  
  8.                 WHEN SCORE – (select avg(SCORE) from stdscore where CNAME=‘JAVA’) > 20 THEN  
  9.                     ‘优秀’  
  10.                 WHEN SCORE – (select avg(SCORE) from stdscore where CNAME=‘JAVA’) > 10 THEN  
  11.                     ‘良好’  
  12.                 WHEN SCORE – (select avg(SCORE) from stdscore where CNAME=‘JAVA’) >= 0 THEN  
  13.                     ‘普通’  
  14.                 ELSE  
  15.                     ‘较差’  
  16.                 END  
  17.             )  
  18.         END  
  19.     ) JAVA,  
  20.     MAX(  
  21.         CASE CNAME  
  22.         WHEN ‘mysql’ THEN  
  23.             (  
  24.                 CASE  
  25.                 WHEN SCORE – (select avg(SCORE) from stdscore where CNAME=‘JAVA’) > 20 THEN  
  26.                     ‘优秀’  
  27.                 WHEN SCORE – (select avg(SCORE) from stdscore where CNAME=‘JAVA’) > 10 THEN  
  28.                     ‘良好’  
  29.                 WHEN SCORE – (select avg(SCORE) from stdscore where CNAME=‘JAVA’) >= 0 THEN  
  30.                     ‘普通’  
  31.                 ELSE  
  32.                     ‘较差’  
  33.                 END  
  34.             )  
  35.         END  
  36.     ) mysql  
  37. FROM  
  38.     stdscore  
  39. GROUP BY  
  40.     SNAME;  



方法二:

[sql] 
view plain  
copy

  1. SELECT DISTINCT  a.sname,  
  2. (SELECT (  
  3.                 CASE  
  4.                 WHEN SCORE – (select avg(SCORE) from stdscore where CNAME=‘JAVA’) > 20 THEN  
  5.                     ‘优秀’  
  6.                 WHEN SCORE – (select avg(SCORE) from stdscore where CNAME=‘JAVA’) > 10 THEN  
  7.                     ‘良好’  
  8.                 WHEN SCORE – (select avg(SCORE) from stdscore where CNAME=‘JAVA’) >= 0 THEN  
  9.                     ‘普通’  
  10.                 ELSE  
  11.                     ‘较差’  
  12.                 END  
  13.             ) FROM stdscore b WHERE a.sname=b.sname AND b.CNAME=‘JAVA’ ) AS ‘JAVA’,  
  14. (SELECT (  
  15.                 CASE  
  16.                 WHEN SCORE – (select avg(SCORE) from stdscore where CNAME=‘JAVA’) > 20 THEN  
  17.                     ‘优秀’  
  18.                 WHEN SCORE – (select avg(SCORE) from stdscore where CNAME=‘JAVA’) > 10 THEN  
  19.                     ‘良好’  
  20.                 WHEN SCORE – (select avg(SCORE) from stdscore where CNAME=‘JAVA’) >= 0 THEN  
  21.                     ‘普通’  
  22.                 ELSE  
  23.                     ‘较差’  
  24.                 END  
  25.             ) FROM stdscore b WHERE a.sname=b.sname AND b.CNAME=‘mysql’ ) AS ‘mysql’  
  26. FROM stdscore a  



方法三:

[sql] 
view plain  
copy

  1. DROP PROCEDURE  
  2. IF EXISTS sp_score;  
  3. DELIMITER &&  
  4.   
  5. CREATE PROCEDURE sp_score ()  
  6. BEGIN  
  7.     #课程名称  
  8.     DECLARE  
  9.         cname_n VARCHAR (20) ; #所有课程数量  
  10.         DECLARE  
  11.             count INT ; #计数器  
  12.             DECLARE  
  13.                 i INT DEFAULT 0 ; #拼接SQL字符串  
  14.             SET @s = ‘SELECT sname’ ;  
  15.             SET count = (  
  16.                 SELECT  
  17.                     COUNT(DISTINCT cname)  
  18.                 FROM  
  19.                     stdscore  
  20.             ) ;  
  21.             WHILE i < count DO  
  22.   
  23.   
  24.             SET cname_n = (  
  25.                 SELECT  
  26.                     cname  
  27.                 FROM  
  28.                     stdscore  
  29.         GROUP BY CNAME   
  30.                 LIMIT i, 1  
  31.             ) ;  
  32.             SET @s = CONCAT(  
  33.                 @s,  
  34.                 ‘, MAX(CASE cname WHEN ‘,  
  35.                 ‘\”,  
  36.                 cname_n,  
  37.                 ‘\”,  
  38.                 ‘ THEN (  
  39.                 CASE  
  40.                 WHEN SCORE – (select avg(SCORE) from stdscore where CNAME=\”,cname_n,‘\’) > 20 THEN  
  41.                     \‘优秀\’  
  42.                 WHEN SCORE – (select avg(SCORE) from stdscore where CNAME=\”,cname_n,‘\’) > 10 THEN  
  43.                     \‘良好\’  
  44.                 WHEN SCORE – (select avg(SCORE) from stdscore where CNAME=\”,cname_n,‘\’) >= 0 THEN  
  45.                     \‘普通\’  
  46.                 ELSE  
  47.                     \‘较差\’  
  48.                 END  
  49.             ) END)’,  
  50.                 ‘ AS ‘,  
  51.                 ‘\”,  
  52.                 cname_n,  
  53.                 ‘\”  
  54.             ) ;  
  55.             SET i = i + 1 ;  
  56.             END  
  57.             WHILE ;  
  58.             SET @s = CONCAT(  
  59.                 @s,  
  60.                 ‘ FROM stdscore GROUP BY sname’  
  61.             ) ;   
  62.             #用于调试  
  63.             #SELECT @s;  
  64.             PREPARE stmt  
  65.             FROM  
  66.                 @s ; EXECUTE stmt ;  
  67.             END&&  
  68.   
  69.   
  70. CALL sp_score ();  

几种方法比较分析

第一种使用了分组,对每个课程分别处理。

第二种方法使用了表连接。

第三种使用了存储过程,实际上可以是第一种或第二种方法的动态化,先计算出所有课程的数量,然后对每个分组进行课程查询。
这种方法的一个最大的好处是当新增了一门课程时,SQL语句不需要重写。

小结

关于行转列和列转行

这个概念似乎容易弄混,有人把行转列理解为列转行,有人把列转行理解为行转列;

这里做个定义:

行转列:把表中特定列(如本文中的:CNAME)的数据去重后做为列名(如查询结果行中的“Java,mysql”,处理后是做为列名输出);

列转行:可以说是行转列的反转,把表中特定列(如本文处理结果中的列名“JAVA,mysql”)做为每一行数据对应列“CNAME”的值;

关于效率

不知道有什么好的生成模拟数据的方法或工具,麻烦小伙伴推荐一下,抽空我做一下对比;

还有其它更好的方法吗?

本文使用的几种方法应该都有优化的空间,特别是使用存储过程的话会更加灵活,功能更强大;

本文的分级只是给出一种思路,分级的方法如果学生的成绩相差较小的话将失去意义;

如果小伙伴有更好的方法,还请不吝赐教,感激不尽!

有些需求可能不需要联合主键

有些需求可能不需要联合主键,因为一门课程可能允许学生考多次,取最好的一次成绩,或者取多次的平均成绩。


原文地址:
http://blog.csdn.net/testcs_dn/article/details/49847299

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/132379.html原文链接:https://javaforall.net

赞 (0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • IDEA 2020.1 最新破解教程,有效期到2089年!

    IDEA 2020.1 最新破解教程,有效期到2089年!

    2020年11月14日
  • word2vec原理总结

    word2vec原理总结CBOW与Skip-Gram模型基础:https://www.cnblogs.com/pinard/p/7160330.htmlHierarchicalSoftmax的模型:https://www.cnblogs.com/pinard/p/7243513.htmlNegativeSampling的模型:https://www.cnblogs.com/pinard/p/7249903.h…

    2022年5月17日
  • 如何判断二极管的极性_二极管的反向饱和电流

    如何判断二极管的极性_二极管的反向饱和电流三极管饱和状态的判断比如上图如何判断电路正常状态下(麦克风无声音)Q1饱和而不是Q2饱和?首先看Q1:因为R2=100R3所以电流的话R3是100倍的R2,但是8050放大系数是200倍以上,说明此时三极管已经无力再放大这个基极电流了,三极管处于饱和状态而后边Q2R3是R4的30倍左右,很明显电流的话根本就用不上HFE放大系统,处于截止状态…

    2022年9月11日
  • layDate时间控件

    layDate时间控件给大家介绍一款超好用的控件layDate控件,layui.laydate下载类库之后直接使用,在这里我先展示效果图:使用起来也是很简单,下载好类库,把类库粘贴到你的项目中类库提取码:v74d代码:<!DOCTYPEhtml><html><head><metacharset=”utf-8″><title>使用layDate独立版</title></head><body>

    2022年5月10日
  • python字符串转义字符_python 转义

    python字符串转义字符_python 转义上图中因为python不知到如何处理一对单引号之后的内容,不能识别第三个单引号。(默认都是以一对单引号或双引号来表示字符串从结束到开始。)长字符串:用三个引号(单引号或者双引号)来代表字符串开始和结束例如在有些情况下,就需要用转义:(1)>>>path=’c:\nowhere’输出的内容换行,不是想要的结果,我们就需要转义用反斜杠\来转义…

    2022年10月23日
  • mac idea 2021.5 激活码【在线注册码/序列号/破解码】

    mac idea 2021.5 激活码【在线注册码/序列号/破解码】,https://javaforall.net/100143.html。详细ieda激活码不妨到全栈程序员必看教程网一起来了解一下吧!

    2022年3月18日

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号